From a579f4400d0c9f5e8356a7b10830bc5a2caf8aa5 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 12 Aug 2022 17:57:09 +0000 Subject: [PATCH 001/428] Build fiddle with WASMFS OPFS support and attempt to use it if available. It does not work because of an inexplicable exception in Emscripten-generated code and perpetually-locked db, but it's not yet clear why. FossilOrigin-Name: a16f0a46ec88c560f73d5664e4bf53fb5dd1a22e99a92c11b5c8d784816c3282 --- Makefile.in | 12 +- ext/wasm/EXPORTED_FUNCTIONS.fiddle | 2 + ext/wasm/GNUmakefile | 4 +- ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api | 2 + ext/wasm/api/sqlite3-wasm.c | 46 +++ ext/wasm/fiddle/fiddle-worker.js | 433 ++++++++++---------- ext/wasm/fiddle/fiddle.js | 2 +- manifest | 27 +- manifest.uuid | 2 +- 9 files changed, 303 insertions(+), 227 deletions(-) diff --git a/Makefile.in b/Makefile.in index 8fac5a52d3..6e39a5c46b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1540,25 +1540,29 @@ fiddle_module_js = $(fiddle_dir)/fiddle-module.js #emcc_opt = -O2 #emcc_opt = -O3 emcc_opt = -Oz +emcc_flags_opfs = -sWASMFS -pthread emcc_flags = $(emcc_opt) \ -sALLOW_TABLE_GROWTH \ -sABORTING_MALLOC \ -sSTRICT_JS \ - -sENVIRONMENT=web \ + -sENVIRONMENT=web,worker \ -sMODULARIZE \ -sEXPORTED_RUNTIME_METHODS=@$(wasm_dir_abs)/EXPORTED_RUNTIME_METHODS.fiddle \ -sDYNAMIC_EXECUTION=0 \ --minify 0 \ -I. $(SHELL_OPT) \ - -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_UTF16 -DSQLITE_OMIT_DEPRECATED -$(fiddle_module_js): Makefile sqlite3.c shell.c \ + -DSQLITE_THREADSAFE=0 \ + -DSQLITE_OMIT_UTF16 \ + -DSQLITE_OMIT_DEPRECATED \ + $(emcc_flags_opfs) +$(fiddle_module_js): Makefile $(wasm_dir)/api/sqlite3-wasm.c shell.c \ $(wasm_dir)/EXPORTED_RUNTIME_METHODS.fiddle \ $(wasm_dir)/EXPORTED_FUNCTIONS.fiddle emcc -o $@ $(emcc_flags) \ -sEXPORT_NAME=initFiddleModule \ -sEXPORTED_FUNCTIONS=@$(wasm_dir_abs)/EXPORTED_FUNCTIONS.fiddle \ -DSQLITE_SHELL_FIDDLE \ - sqlite3.c shell.c + $(wasm_dir)/api/sqlite3-wasm.c shell.c gzip < $@ > $@.gz gzip < $(fiddle_dir)/fiddle-module.wasm > $(fiddle_dir)/fiddle-module.wasm.gz $(fiddle_dir)/fiddle.js.gz: $(fiddle_dir)/fiddle.js diff --git a/ext/wasm/EXPORTED_FUNCTIONS.fiddle b/ext/wasm/EXPORTED_FUNCTIONS.fiddle index b96ce4e67c..602d612548 100644 --- a/ext/wasm/EXPORTED_FUNCTIONS.fiddle +++ b/ext/wasm/EXPORTED_FUNCTIONS.fiddle @@ -5,3 +5,5 @@ _fiddle_the_db _fiddle_db_arg _fiddle_db_filename _fiddle_reset_db +_sqlite3_wasm_init_opfs +_sqlite3_wasm_vfs_unlink diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index ee8ade74a3..e32febbfc3 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -168,12 +168,13 @@ emcc.cflags := emcc.cflags += -std=c99 -fPIC # -------------^^^^^^^^ we currently need c99 for WASM-specific sqlite3 APIs. emcc.cflags += -I. -I$(dir.top) # $(SQLITE_OPT) +emcc.cflags += -pthread ######################################################################## # emcc flags specific to building the final .js/.wasm file... emcc.jsflags := -fPIC emcc.jsflags += --no-entry -emcc.jsflags += -sENVIRONMENT=web +emcc.jsflags += -sENVIRONMENT=web,worker emcc.jsflags += -sMODULARIZE emcc.jsflags += -sSTRICT_JS emcc.jsflags += -sDYNAMIC_EXECUTION=0 @@ -182,6 +183,7 @@ emcc.jsflags += -sEXPORTED_FUNCTIONS=@$(dir.wasm)/EXPORTED_FUNCTIONS.api emcc.jsflags += -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory # wasmMemory==>for -sIMPORTED_MEMORY emcc.jsflags += -sUSE_CLOSURE_COMPILER=0 emcc.jsflags += -sIMPORTED_MEMORY +emcc.jsflags += -pthread -sWASMFS #emcc.jsflags += -sINITIAL_MEMORY=13107200 #emcc.jsflags += -sTOTAL_STACK=4194304 emcc.jsflags += -sEXPORT_NAME=sqlite3InitModule diff --git a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api index 8f103c7c0b..4a6b71b8ea 100644 --- a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api +++ b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api @@ -68,5 +68,7 @@ _sqlite3_vfs_find _sqlite3_vfs_register _sqlite3_wasm_db_error _sqlite3_wasm_enum_json +_sqlite3_wasm_init_opfs +_sqlite3_wasm_vfs_unlink _malloc _free diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index 6a81da3e5f..30a9dafeb8 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -411,3 +411,49 @@ const char * sqlite3_wasm_enum_json(void){ #undef outf #undef lenCheck } + +/* +** This function is NOT part of the sqlite3 public API. It is strictly +** for use by the sqlite project's own JS/WASM bindings. +** +** This function invokes the xDelete method of the default VFS, +** passing on the given filename. If zName is NULL, no default VFS is +** found, or it has no xDelete method, SQLITE_MISUSE is returned, else +** the result of the xDelete() call is returned. +*/ +int sqlite3_wasm_vfs_unlink(const char * zName){ + int rc = SQLITE_MISUSE /* ??? */; + sqlite3_vfs * const pVfs = sqlite3_vfs_find(0); + if( zName && pVfs && pVfs->xDelete ){ + rc = pVfs->xDelete(pVfs, zName, 1); + } + return rc; +} + +#ifdef __EMSCRIPTEN__ +#include +#include +/* +** This function is NOT part of the sqlite3 public API. It is strictly +** for use by the sqlite project's own JS/WASM bindings. +** +** This function should only be called if the JS side detects the +** existence of the Origin-Private FileSystem (OPFS) APIs in the +** client. The first time it is called, this function instantiates a +** WASMFS backend impl for OPFS. On success, subsequent calls are +** no-ops. +** +** Returns 0 on success, SQLITE_NOMEM if intantiation of the backend +** object fails. +*/ +int sqlite3_wasm_init_opfs(void){ + static backend_t pOpfs = 0; + if( !pOpfs ){ + pOpfs = wasmfs_create_opfs_backend(); + if( pOpfs ){ + emscripten_console_log("Created OPFS WASMFS backend."); + } + } + return pOpfs ? 0 : SQLITE_NOMEM; +} +#endif /* __EMSCRIPTEN__ */ diff --git a/ext/wasm/fiddle/fiddle-worker.js b/ext/wasm/fiddle/fiddle-worker.js index ca562323ce..fb9fd30229 100644 --- a/ext/wasm/fiddle/fiddle-worker.js +++ b/ext/wasm/fiddle/fiddle-worker.js @@ -89,213 +89,230 @@ */ "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 && oldName !== fn){ - try{fiddleModule.FS.unlink(oldName);} - catch(e){/*ignored*/} - } - 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'); + /** + 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 && oldName !== fn){ + try{fiddleModule.fsUnlink(oldName);} + catch(e){/*ignored*/} + } + 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){ + fiddleModule.fsUnlink = fiddleModule.cwrap('sqlite3_wasm_vfs_unlink','number',['string']); + (function initOpfs(){ + if(!self.FileSystemHandle || !self.FileSystemDirectoryHandle + || !self.FileSystemFileHandle){ + stdout("OPFS unavailable. All DB state is transient."); + return; + } + try { + if(0===fiddleModule.ccall('sqlite3_wasm_init_opfs', undefined)){ + stdout("Initialized OPFS WASMFS backend."); + }else{ + stderr("Initialization of OPFS WASMFS backend failed."); + } + }catch(e){ + stderr("Apparently missing WASMFS:",e.message); + } + })(); + wMsg('fiddle-ready'); + }); })(); diff --git a/ext/wasm/fiddle/fiddle.js b/ext/wasm/fiddle/fiddle.js index 619ce4eca8..3fd36bff86 100644 --- a/ext/wasm/fiddle/fiddle.js +++ b/ext/wasm/fiddle/fiddle.js @@ -775,7 +775,7 @@ SELECT group_concat(rtrim(t),x'0a') as Mandelbrot FROM a;`} }); })()/* example queries */; - SF.echo(null/*clear any output generated by the init process*/); + //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(); diff --git a/manifest b/manifest index ef72de5c1d..10191bf4da 100644 --- a/manifest +++ b/manifest @@ -1,9 +1,9 @@ -C Minor\swasm-related\sdoc\sclarification\sand\sremove\san\sobsolete\scode\scomment. -D 2022-08-12T17:55:18.237 +C Build\sfiddle\swith\sWASMFS\sOPFS\ssupport\sand\sattempt\sto\suse\sit\sif\savailable.\sIt\sdoes\snot\swork\sbecause\sof\san\sinexplicable\sexception\sin\sEmscripten-generated\scode\sand\sperpetually-locked\sdb,\sbut\sit's\snot\syet\sclear\swhy. +D 2022-08-12T17:57:09.467 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in eceb228bf7b48f961b59a508f42ffa1211bf0c4c5bc818807768cc7b187ab0c8 +F Makefile.in bada766cb44d202e405eca68e32bb3bcf11cad5b85c75f7fb16ac39e863f03f5 F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 F Makefile.msc d547a2fdba38a1c6cd1954977d0b0cc017f5f8fbfbc65287bf8d335808938016 F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e @@ -472,11 +472,11 @@ F ext/session/test_session.c f433f68a8a8c64b0f5bc74dc725078f12483301ad4ae8375205 F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb -F ext/wasm/EXPORTED_FUNCTIONS.fiddle 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3 +F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 5359a37fc13b68fad2259228590450339a0c59687744edd0db7bb93d3b1ae2b1 +F ext/wasm/GNUmakefile c354ad5ce3ecec80c28ed4314827adc10cbbc0ef47e028b0cfbdf336c4a98fa4 F ext/wasm/README.md 4b00ae7c7d93c4591251245f0996a319e2651361013c98d2efb0b026771b7331 -F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api c5eaceabb9e759aaae7d3101a4a3e542f96ab2c99d89a80ce20ec18c23115f33 +F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 F ext/wasm/api/README.md b6d0fb64bfdf7bf9ce6938ea4104228f6f5bbef600f5d910b2f8c8694195988c F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c @@ -488,16 +488,16 @@ F ext/wasm/api/sqlite3-api-opfs.js c93cdd14f81a26b3a64990515ee05c7e29827fbc8fba4 F ext/wasm/api/sqlite3-api-prologue.js 0fb0703d2d8ac89fa2d4dd8f9726b0ea226b8708ac34e5b482df046e147de0eb F ext/wasm/api/sqlite3-api-worker.js 1124f404ecdf3c14d9f829425cef778cd683911a9883f0809a463c3c7773c9fd F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 -F ext/wasm/api/sqlite3-wasm.c 8585793ca8311c7a0618b7e00ed2b3729799c20664a51f196258576e3d475c9e +F ext/wasm/api/sqlite3-wasm.c 0b3f56078f3e3806fb6dfbc756198d1634d3bfdbf3677c77702cf453c4ed336a F ext/wasm/api/sqlite3-worker.js 1325ca8d40129a82531902a3a077b795db2eeaee81746e5a0c811a04b415fa7f F ext/wasm/common/SqliteTestUtil.js e41a1406f18da9224523fad0c48885caf995b56956a5b9852909c0989e687e90 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 F ext/wasm/common/whwasmutil.js 3d9deda1be718e2b10e2b6b474ba6ba857d905be314201ae5b3df5eef79f66aa F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f -F ext/wasm/fiddle/fiddle-worker.js 88bc2193a6cb6a3f04d8911bed50a4401fe6f277de7a71ba833865ab64a1b4ae +F ext/wasm/fiddle/fiddle-worker.js c9d66230269cc26d33aa84501ae601c24f8697c3711be5d769c4c687bfcaba8f F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 -F ext/wasm/fiddle/fiddle.js 812f9954cc7c4b191884ad171f36fcf2d0112d0a7ecfdf6087896833a0c079a8 +F ext/wasm/fiddle/fiddle.js bef4b30e078445a7cd2255fba07acd083aa1c3cc074a73b38ea847fd340f1adc F ext/wasm/jaccwabyt/jaccwabyt.js 99b424b4d467d4544e82615b58e2fe07532a898540bf9de2a985f3c21e7082b2 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f @@ -1999,8 +1999,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 4c10b9b18feca82440273f8192fef951ad051bbfd8aad4d3c840cf6375130afd -R 3e055b525bbc2100d8c4c001b630489b +P 1b1f650a08da93da97ed3a96b9a3e4eac567472c266188c02a9bffe1cf620e53 +R a893d12114f31061d4e8d4e31dd72df1 +T *branch * fiddle-opfs +T *sym-fiddle-opfs * +T -sym-trunk * Cancelled\sby\sbranch. U stephan -Z 4d766400503717808861a1e631c17801 +Z e55c83b0eefdc75c613c24a829476f39 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 911760f4e7..ec39b043b1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1b1f650a08da93da97ed3a96b9a3e4eac567472c266188c02a9bffe1cf620e53 \ No newline at end of file +a16f0a46ec88c560f73d5664e4bf53fb5dd1a22e99a92c11b5c8d784816c3282 \ No newline at end of file From 24ddbca8d3b9991e179d4e12507499c4e7529065 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 12 Aug 2022 18:07:22 +0000 Subject: [PATCH 002/428] Updated fiddle deps to ensure that sqlite3.c is built first. FossilOrigin-Name: f0ca02611484f5031076d7fae88845e9931715e9108ec0572629200d4f6636ef --- Makefile.in | 6 ++++-- manifest | 15 ++++++--------- manifest.uuid | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Makefile.in b/Makefile.in index 6e39a5c46b..d381b24d21 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1535,6 +1535,8 @@ wasm_dir_abs = $(TOP)/ext/wasm fiddle_dir = $(wasm_dir)/fiddle fiddle_dir_abs = $(TOP)/$(fiddle_dir) fiddle_module_js = $(fiddle_dir)/fiddle-module.js +sqlite3_wasm_c = $(wasm_dir)/api/sqlite3-wasm.c +$(sqlite3_wasm_c): sqlite3.c #emcc_opt = -O0 #emcc_opt = -O1 #emcc_opt = -O2 @@ -1555,14 +1557,14 @@ emcc_flags = $(emcc_opt) \ -DSQLITE_OMIT_UTF16 \ -DSQLITE_OMIT_DEPRECATED \ $(emcc_flags_opfs) -$(fiddle_module_js): Makefile $(wasm_dir)/api/sqlite3-wasm.c shell.c \ +$(fiddle_module_js): Makefile $(sqlite3_wasm_c) shell.c \ $(wasm_dir)/EXPORTED_RUNTIME_METHODS.fiddle \ $(wasm_dir)/EXPORTED_FUNCTIONS.fiddle emcc -o $@ $(emcc_flags) \ -sEXPORT_NAME=initFiddleModule \ -sEXPORTED_FUNCTIONS=@$(wasm_dir_abs)/EXPORTED_FUNCTIONS.fiddle \ -DSQLITE_SHELL_FIDDLE \ - $(wasm_dir)/api/sqlite3-wasm.c shell.c + $(sqlite3_wasm_c) shell.c gzip < $@ > $@.gz gzip < $(fiddle_dir)/fiddle-module.wasm > $(fiddle_dir)/fiddle-module.wasm.gz $(fiddle_dir)/fiddle.js.gz: $(fiddle_dir)/fiddle.js diff --git a/manifest b/manifest index 10191bf4da..1082b84cf6 100644 --- a/manifest +++ b/manifest @@ -1,9 +1,9 @@ -C Build\sfiddle\swith\sWASMFS\sOPFS\ssupport\sand\sattempt\sto\suse\sit\sif\savailable.\sIt\sdoes\snot\swork\sbecause\sof\san\sinexplicable\sexception\sin\sEmscripten-generated\scode\sand\sperpetually-locked\sdb,\sbut\sit's\snot\syet\sclear\swhy. -D 2022-08-12T17:57:09.467 +C Updated\sfiddle\sdeps\sto\sensure\sthat\ssqlite3.c\sis\sbuilt\sfirst. +D 2022-08-12T18:07:22.516 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in bada766cb44d202e405eca68e32bb3bcf11cad5b85c75f7fb16ac39e863f03f5 +F Makefile.in a4002278db76f5f959d73bf3fedfd1b1c24ca198e150eadb327a7daa597cce6a F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 F Makefile.msc d547a2fdba38a1c6cd1954977d0b0cc017f5f8fbfbc65287bf8d335808938016 F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e @@ -1999,11 +1999,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 1b1f650a08da93da97ed3a96b9a3e4eac567472c266188c02a9bffe1cf620e53 -R a893d12114f31061d4e8d4e31dd72df1 -T *branch * fiddle-opfs -T *sym-fiddle-opfs * -T -sym-trunk * Cancelled\sby\sbranch. +P a16f0a46ec88c560f73d5664e4bf53fb5dd1a22e99a92c11b5c8d784816c3282 +R 68c3995263b343f69486af69573b8b41 U stephan -Z e55c83b0eefdc75c613c24a829476f39 +Z 571a8d3aad5e0e97b4f122d411b8daa9 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ec39b043b1..01bd43411b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a16f0a46ec88c560f73d5664e4bf53fb5dd1a22e99a92c11b5c8d784816c3282 \ No newline at end of file +f0ca02611484f5031076d7fae88845e9931715e9108ec0572629200d4f6636ef \ No newline at end of file From 2cf599cff887bee309e0ac12eb68b487346474a3 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 13 Aug 2022 13:42:07 +0000 Subject: [PATCH 003/428] Corrected TextDecoder.decode() usage to run when its input references a SharedArrayBuffer. FossilOrigin-Name: d4d773405c579e7efd95be8d81fe14d71218e62e44c523d38e02f89424ba6ce8 --- ext/wasm/common/whwasmutil.js | 24 ++++++++++++++++-------- ext/wasm/jaccwabyt/jaccwabyt.js | 15 ++++++++++++--- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 36 insertions(+), 19 deletions(-) diff --git a/ext/wasm/common/whwasmutil.js b/ext/wasm/common/whwasmutil.js index 5a1d425caf..e51f690e29 100644 --- a/ext/wasm/common/whwasmutil.js +++ b/ext/wasm/common/whwasmutil.js @@ -669,6 +669,18 @@ self.WhWasmUtilInstaller = function(target){ return pos - ptr; }; + /** Internal helper to use in operations which need to distinguish + between SharedArrayBuffer heap memory and non-shared heap. */ + const __SAB = ('undefined'===typeof SharedArrayBuffer) + ? function(){} : SharedArrayBuffer; + const __utf8Decode = function(arrayBuffer, begin, end){ + return cache.utf8Decoder.decode( + (arrayBuffer.buffer instanceof __SAB) + ? arrayBuffer.slice(begin, end) + : arrayBuffer.subarray(begin, end) + ); + }; + /** Expects ptr to be a pointer into the WASM heap memory which refers to a NUL-terminated C-style string encoded as UTF-8. This @@ -678,11 +690,7 @@ self.WhWasmUtilInstaller = function(target){ */ target.cstringToJs = function(ptr){ const n = this.cstrlen(ptr); - if(null===n) return n; - return n - ? cache.utf8Decoder.decode( - new Uint8Array(heapWrappers().HEAP8U.buffer, ptr, n) - ) : ""; + return n ? __utf8Decode(heapWrappers().HEAP8U, ptr, ptr+n) : (null===n ? n : ""); }.bind(target); /** @@ -1070,11 +1078,11 @@ self.WhWasmUtilInstaller = function(target){ /** Looks up a WASM-exported function named fname from - target.exports. If found, it is called, passed all remaining + target.exports. If found, it is called, passed all remaining arguments, and its return value is returned to xCall's caller. If not found, an exception is thrown. This function does no - conversion of argument or return types, but see xWrap() - and xCallWrapped() for variants which do. + conversion of argument or return types, but see xWrap() and + xCallWrapped() for variants which do. As a special case, if passed only 1 argument after the name and that argument in an Array, that array's entries become the diff --git a/ext/wasm/jaccwabyt/jaccwabyt.js b/ext/wasm/jaccwabyt/jaccwabyt.js index a018658579..14c93b3a2e 100644 --- a/ext/wasm/jaccwabyt/jaccwabyt.js +++ b/ext/wasm/jaccwabyt/jaccwabyt.js @@ -394,7 +394,17 @@ self.Jaccwabyt = function StructBinderFactory(config){ const __utf8Decoder = new TextDecoder('utf-8'); const __utf8Encoder = new TextEncoder(); - + /** Internal helper to use in operations which need to distinguish + between SharedArrayBuffer heap memory and non-shared heap. */ + const __SAB = ('undefined'===typeof SharedArrayBuffer) + ? function(){} : SharedArrayBuffer; + const __utf8Decode = function(arrayBuffer, begin, end){ + return __utf8Decoder.decode( + (arrayBuffer.buffer instanceof __SAB) + ? arrayBuffer.slice(begin, end) + : arrayBuffer.subarray(begin, end) + ); + }; /** Uses __lookupMember() to find the given obj.structInfo key. Returns that member if it is a string, else returns false. If the @@ -437,8 +447,7 @@ self.Jaccwabyt = function StructBinderFactory(config){ //log("mem[",pos,"]",mem[pos]); }; //log("addr =",addr,"pos =",pos); - if(addr===pos) return ""; - return __utf8Decoder.decode(new Uint8Array(mem.buffer, addr, pos-addr)); + return (addr===pos) ? "" : __utf8Decode(mem, addr, pos); }; /** diff --git a/manifest b/manifest index 83bed6954d..1c235509d7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Use\snew\s-DSQLITE_DEFAULT_UNIX_VFS="unix-none"\sfor\sfiddle\sbuild\sto\sbypass\sOPFS\slocking\serrors. -D 2022-08-12T18:54:08.424 +C Corrected\sTextDecoder.decode()\susage\sto\srun\swhen\sits\sinput\sreferences\sa\sSharedArrayBuffer. +D 2022-08-13T13:42:07.359 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -493,12 +493,12 @@ F ext/wasm/api/sqlite3-worker.js 1325ca8d40129a82531902a3a077b795db2eeaee81746e5 F ext/wasm/common/SqliteTestUtil.js e41a1406f18da9224523fad0c48885caf995b56956a5b9852909c0989e687e90 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 -F ext/wasm/common/whwasmutil.js 3d9deda1be718e2b10e2b6b474ba6ba857d905be314201ae5b3df5eef79f66aa +F ext/wasm/common/whwasmutil.js 41b8e097e0a9cb07c24c0ede3c81b72470a63f4a4efb07f75586dc131569f5ae F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/fiddle/fiddle-worker.js c9d66230269cc26d33aa84501ae601c24f8697c3711be5d769c4c687bfcaba8f F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js bef4b30e078445a7cd2255fba07acd083aa1c3cc074a73b38ea847fd340f1adc -F ext/wasm/jaccwabyt/jaccwabyt.js 99b424b4d467d4544e82615b58e2fe07532a898540bf9de2a985f3c21e7082b2 +F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19 @@ -1999,8 +1999,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f0ca02611484f5031076d7fae88845e9931715e9108ec0572629200d4f6636ef 49828bdec5f926cd18a069d39a5db0b1e1f3528a2affcfbaa1cf7b98aca51b3b -R cdc00c7ef64a776b22c16fe2546768af +P b3a93ec75acb38535691d6eaceb5a1a218a5ee3f755a1e12c9255b90567fc795 +R 243b6e88d1ed7db99fd955f733548f1c U stephan -Z abf4f77a1cc7fc6241b9153ec6b252c6 +Z ddf468f7ec78d048a46e361913445144 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index fd28b4f82d..ab645ba532 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b3a93ec75acb38535691d6eaceb5a1a218a5ee3f755a1e12c9255b90567fc795 \ No newline at end of file +d4d773405c579e7efd95be8d81fe14d71218e62e44c523d38e02f89424ba6ce8 \ No newline at end of file From f79451eea7a9c28ddb9ec0074d31dc9c83d7c83f Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 13 Aug 2022 13:46:19 +0000 Subject: [PATCH 004/428] wasm OO API #1: added DB.callInTransaction() and Stmt.stepFinalize(). FossilOrigin-Name: e37dddc1dd9c0530e4b1c6cb0ca7cba7451caa37734d383c9b47f378d7222242 --- ext/wasm/api/sqlite3-api-oo1.js | 67 +++++++++++++++++++++++++++++---- manifest | 12 +++--- manifest.uuid | 2 +- 3 files changed, 67 insertions(+), 14 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 9e54733966..b6e63d540a 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -575,8 +575,8 @@ while(stmt.step()){ stmt._isLocked = true; const row = arg.cbArg(stmt); - if(callback) callback(row, stmt); if(resultRows) resultRows.push(row); + if(callback) callback(row, stmt); stmt._isLocked = false; } rowMode = undefined; @@ -819,6 +819,28 @@ return this.pointer ? Object.keys(__stmtMap.get(this)).length : 0; }, + /** + Starts a transaction, calls the given callback, and then either + rolls back or commits the transaction, depending on whether the + callback throw. The callback is pass this db object as its only + argument. On success, returns the result of the callback. + Throws on error. + */ + callInTransaction: function(callback){ + affirmDbOpen(this); + let err, rc; + this.exec("BEGIN"); + try { rc = callback(this); } + catch(e){ + err = e; + throw e; + }finally{ + if(err) this.exec("ROLLBACK"); + else this.exec("COMMIT"); + } + return rc; + }, + /** This function currently does nothing and always throws. It WILL BE REMOVED pending other refactoring, to eliminate a hard @@ -1042,7 +1064,7 @@ console.warn("Unsupported bind() argument type:",val); toss3("Unsupported bind() argument type: "+(typeof val)); } - if(rc) checkDbRc(stmt.db.pointer, rc); + if(rc) DB.checkRc(stmt.db.pointer, rc); return stmt; }; @@ -1228,9 +1250,10 @@ return this; }, /** - 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. + Steps the statement one time. If the result indicates that a + row of data is available, a truthy value is returned. + If no row of data is available, a falsy + value is returned. Throws on error. */ step: function(){ affirmUnlocked(this, 'step()'); @@ -1242,8 +1265,38 @@ this._mayGet = false; console.warn("sqlite3_step() rc=",rc,"SQL =", capi.sqlite3_sql(this.pointer)); - checkDbRc(this.db.pointer, rc); - }; + DB.checkRc(this.db.pointer, rc); + } + }, + /** + Functions like step() except that + it finalizes this statement immediately after stepping unless + the step cannot be performed because the statement is + locked. Throws on error, but any error other than the + statement-is-locked case will also trigger finalization of this + statement. + + On success, it returns true if the step indicated that a row of + data was available, else it returns false. + + This is intended to simplify use cases such as: + + ``` + aDb.prepare("insert in foo(a) values(?)").bind(123).stepFinalize(); + ``` + */ + stepFinalize: function(){ + affirmUnlocked(this, 'step()'); + const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer); + switch(rc){ + case capi.SQLITE_DONE: this.finalize(); return false; + case capi.SQLITE_ROW: this.finalize(); return true; + default: + this.finalize(); + console.warn("sqlite3_step() rc=",rc,"SQL =", + capi.sqlite3_sql(this.pointer)); + DB.checkRc(this.db.pointer, rc); + } }, /** Fetches the value from the given 0-based column index of diff --git a/manifest b/manifest index 1c235509d7..e90ec6ca06 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Corrected\sTextDecoder.decode()\susage\sto\srun\swhen\sits\sinput\sreferences\sa\sSharedArrayBuffer. -D 2022-08-13T13:42:07.359 +C wasm\sOO\sAPI\s#1:\sadded\sDB.callInTransaction()\sand\sStmt.stepFinalize(). +D 2022-08-13T13:46:19.722 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -483,7 +483,7 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 149fd63a0400cd1d69548887ffde2ed89c13283384a63c2e9fcfc695e38a9e11 F ext/wasm/api/sqlite3-api-glue.js 82c09f49c69984009ba5af2b628e67cc26c5dd203d383cd3091d40dab4e6514b -F ext/wasm/api/sqlite3-api-oo1.js e9612cb704c0563c5d71ed2a8dccd95bf6394fa4de3115d1b978dc269c49ab02 +F ext/wasm/api/sqlite3-api-oo1.js a3469bbb217b9787ba9aa6216423ec55cf9457fecefb9698e433d0e1cc4cc918 F ext/wasm/api/sqlite3-api-opfs.js c93cdd14f81a26b3a64990515ee05c7e29827fbc8fba4e4c2fef3a37a984db89 F ext/wasm/api/sqlite3-api-prologue.js 0fb0703d2d8ac89fa2d4dd8f9726b0ea226b8708ac34e5b482df046e147de0eb F ext/wasm/api/sqlite3-api-worker.js 1124f404ecdf3c14d9f829425cef778cd683911a9883f0809a463c3c7773c9fd @@ -1999,8 +1999,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P b3a93ec75acb38535691d6eaceb5a1a218a5ee3f755a1e12c9255b90567fc795 -R 243b6e88d1ed7db99fd955f733548f1c +P d4d773405c579e7efd95be8d81fe14d71218e62e44c523d38e02f89424ba6ce8 +R d6004d575cfc0551ba9a0fec437e8b0f U stephan -Z ddf468f7ec78d048a46e361913445144 +Z 111c1d0636763c3347de9dc9f9b9e9af # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ab645ba532..d1493580ab 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d4d773405c579e7efd95be8d81fe14d71218e62e44c523d38e02f89424ba6ce8 \ No newline at end of file +e37dddc1dd9c0530e4b1c6cb0ca7cba7451caa37734d383c9b47f378d7222242 \ No newline at end of file From 90218aec7a0991199b3e06faf723dd482bf970f6 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 13 Aug 2022 13:51:56 +0000 Subject: [PATCH 005/428] Remove OPFS from the fiddle build for the time being - will re-enable once the breakage is figured out via testing with the core API. FossilOrigin-Name: 3bc510a614973eafa60960a99bedb063594a693bdbfd80d7eb480b293b4ab811 --- Makefile.in | 13 +++++++++++-- ext/wasm/fiddle/fiddle-worker.js | 18 +----------------- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 20 insertions(+), 27 deletions(-) diff --git a/Makefile.in b/Makefile.in index 00ea77f045..c9de1403f4 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1542,12 +1542,21 @@ $(sqlite3_wasm_c): sqlite3.c #emcc_opt = -O2 #emcc_opt = -O3 emcc_opt = -Oz -emcc_flags_opfs = -sWASMFS -pthread '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' +emcc_environment = web +# WASMFS OPFS is currently completely broken under fiddle but the +# root cause has not yet been determined. +emcc_flags_opfs = +#emcc_flags_opfs += -sWASMFS -pthread +#emcc_environment = web,worker +#emcc_flags_opfs += -DSQLITE_WASM_OPFS +#emcc_flags_opfs += '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' +#emcc_flags_opfs += -sPTHREAD_POOL_SIZE=2 +#emcc_flags_opfs += -sPTHREAD_POOL_SIZE_STRICT=2 emcc_flags = $(emcc_opt) \ -sALLOW_TABLE_GROWTH \ -sABORTING_MALLOC \ -sSTRICT_JS \ - -sENVIRONMENT=web,worker \ + -sENVIRONMENT=$(emcc_environment) \ -sMODULARIZE \ -sEXPORTED_RUNTIME_METHODS=@$(wasm_dir_abs)/EXPORTED_RUNTIME_METHODS.fiddle \ -sDYNAMIC_EXECUTION=0 \ diff --git a/ext/wasm/fiddle/fiddle-worker.js b/ext/wasm/fiddle/fiddle-worker.js index fb9fd30229..5bc1391753 100644 --- a/ext/wasm/fiddle/fiddle-worker.js +++ b/ext/wasm/fiddle/fiddle-worker.js @@ -296,23 +296,7 @@ emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initFiddleModule */ initFiddleModule(fiddleModule).then(function(thisModule){ - fiddleModule.fsUnlink = fiddleModule.cwrap('sqlite3_wasm_vfs_unlink','number',['string']); - (function initOpfs(){ - if(!self.FileSystemHandle || !self.FileSystemDirectoryHandle - || !self.FileSystemFileHandle){ - stdout("OPFS unavailable. All DB state is transient."); - return; - } - try { - if(0===fiddleModule.ccall('sqlite3_wasm_init_opfs', undefined)){ - stdout("Initialized OPFS WASMFS backend."); - }else{ - stderr("Initialization of OPFS WASMFS backend failed."); - } - }catch(e){ - stderr("Apparently missing WASMFS:",e.message); - } - })(); + thisModule.fsUnlink = thisModule.cwrap('sqlite3_wasm_vfs_unlink','number',['string']); wMsg('fiddle-ready'); }); })(); diff --git a/manifest b/manifest index e90ec6ca06..73a423621e 100644 --- a/manifest +++ b/manifest @@ -1,9 +1,9 @@ -C wasm\sOO\sAPI\s#1:\sadded\sDB.callInTransaction()\sand\sStmt.stepFinalize(). -D 2022-08-13T13:46:19.722 +C Remove\sOPFS\sfrom\sthe\sfiddle\sbuild\sfor\sthe\stime\sbeing\s-\swill\sre-enable\sonce\sthe\sbreakage\sis\sfigured\sout\svia\stesting\swith\sthe\score\sAPI. +D 2022-08-13T13:51:56.967 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in 5d0dfceebf19ccbce323181fa14fe15c3bc470293420a6b680fc76a1b1779087 +F Makefile.in 419791c8e6f5fda6bf33977680e8f3bf782f5f6ec56b5c22567912c7f8fef353 F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 F Makefile.msc d547a2fdba38a1c6cd1954977d0b0cc017f5f8fbfbc65287bf8d335808938016 F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e @@ -495,7 +495,7 @@ F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 F ext/wasm/common/whwasmutil.js 41b8e097e0a9cb07c24c0ede3c81b72470a63f4a4efb07f75586dc131569f5ae F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f -F ext/wasm/fiddle/fiddle-worker.js c9d66230269cc26d33aa84501ae601c24f8697c3711be5d769c4c687bfcaba8f +F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js bef4b30e078445a7cd2255fba07acd083aa1c3cc074a73b38ea847fd340f1adc F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 @@ -1999,8 +1999,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d4d773405c579e7efd95be8d81fe14d71218e62e44c523d38e02f89424ba6ce8 -R d6004d575cfc0551ba9a0fec437e8b0f +P e37dddc1dd9c0530e4b1c6cb0ca7cba7451caa37734d383c9b47f378d7222242 +R 988d3478699d43ef2ce9b5c2bc8119e6 U stephan -Z 111c1d0636763c3347de9dc9f9b9e9af +Z c96da5568e2699a3b63963443a07c191 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d1493580ab..f61125d960 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e37dddc1dd9c0530e4b1c6cb0ca7cba7451caa37734d383c9b47f378d7222242 \ No newline at end of file +3bc510a614973eafa60960a99bedb063594a693bdbfd80d7eb480b293b4ab811 \ No newline at end of file From 9a4c63b0fc85a019d3183abbd7939b081f45d025 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 13 Aug 2022 13:56:00 +0000 Subject: [PATCH 006/428] Cleanups in the wasmfs/opfs integration but disable it in order to get the build into a known-working state before continuing with experimentation. FossilOrigin-Name: 41045be752a5bd7966849638f3ca56f4905308df70f79f2cb6196ca7dce9d525 --- ext/wasm/GNUmakefile | 27 +++++++++------ ext/wasm/api/sqlite3-api-prologue.js | 49 ++++++++++++++++++++++++++++ ext/wasm/api/sqlite3-wasm.c | 27 ++++++++++++--- ext/wasm/testing1.js | 5 +-- ext/wasm/testing2.js | 2 +- manifest | 20 ++++++------ manifest.uuid | 2 +- 7 files changed, 103 insertions(+), 29 deletions(-) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index e32febbfc3..dad77dba0a 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -127,9 +127,9 @@ sqlite3-api.jses := \ $(dir.jacc)/jaccwabyt.js \ $(dir.api)/sqlite3-api-glue.js \ $(dir.api)/sqlite3-api-oo1.js \ - $(dir.api)/sqlite3-api-worker.js \ - $(dir.api)/sqlite3-api-opfs.js \ - $(dir.api)/sqlite3-api-cleanup.js + $(dir.api)/sqlite3-api-worker.js +#sqlite3-api.jses += $(dir.api)/sqlite3-api-opfs.js +sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js sqlite3-api.js := $(dir.api)/sqlite3-api.js CLEAN_FILES += $(sqlite3-api.js) @@ -174,7 +174,6 @@ emcc.cflags += -pthread # emcc flags specific to building the final .js/.wasm file... emcc.jsflags := -fPIC emcc.jsflags += --no-entry -emcc.jsflags += -sENVIRONMENT=web,worker emcc.jsflags += -sMODULARIZE emcc.jsflags += -sSTRICT_JS emcc.jsflags += -sDYNAMIC_EXECUTION=0 @@ -183,7 +182,12 @@ emcc.jsflags += -sEXPORTED_FUNCTIONS=@$(dir.wasm)/EXPORTED_FUNCTIONS.api emcc.jsflags += -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory # wasmMemory==>for -sIMPORTED_MEMORY emcc.jsflags += -sUSE_CLOSURE_COMPILER=0 emcc.jsflags += -sIMPORTED_MEMORY -emcc.jsflags += -pthread -sWASMFS +emcc.environment := -sENVIRONMENT=web +ifeq (0,1) + emcc.jsflags += -pthread -sWASMFS -sPTHREAD_POOL_SIZE=2 + emcc.environment := $(emcc.environment),worker +endif +emcc.jsflags += $(emcc.environment) #emcc.jsflags += -sINITIAL_MEMORY=13107200 #emcc.jsflags += -sTOTAL_STACK=4194304 emcc.jsflags += -sEXPORT_NAME=sqlite3InitModule @@ -235,11 +239,14 @@ $(dir.api)/sqlite3-wasm.o: $(dir.top)/sqlite3.c $(dir.api)/wasm_util.o: emcc.cflags += $(SQLITE_OPT) sqlite3.wasm.c := $(dir.api)/sqlite3-wasm.c \ $(dir.jacc)/jaccwabyt_test.c -# ^^^ FIXME (how?): jaccwabyt_test.c is only needed for the test -# apps. However, we want to test the release builds with those apps, -# so we cannot simply elide that file in release builds. That -# component is critical to the VFS bindings so needs to be tested -# along with the core APIs. +# ^^^ FIXME (how?): jaccwabyt_test.c is only needed for the test apps, +# so we don't really want to include it in release builds. However, we +# want to test the release builds with those apps, so we cannot simply +# elide that file in release builds. That component is critical to the +# VFS bindings so needs to be tested along with the core APIs. +ifneq (,$(filter -sWASMFS,$(emcc.jsflags))) + $(dir.api)/sqlite3-wasm.o: emcc.cflags+=-DSQLITE_WASM_OPFS +endif define WASM_C_COMPILE $(1).o := $$(subst .c,.o,$(1)) sqlite3.wasm.obj += $$($(1).o) diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 60ed61477e..08e3ebf6ed 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -576,6 +576,55 @@ self.sqlite3ApiBootstrap = function(config){ ["sqlite3_total_changes64", "i64", ["sqlite3*"]] ]; + /** State for sqlite3_web_persistent_dir(). */ + let __persistentDir; + /** + An experiment. Do not use. + + If the wasm environment has a persistent storage directory, + its path is returned by this function. If it does not then + it returns one of: + + - `undefined` if initIfNeeded is false and this function has + never been called before. + + - `""` if no persistent storage is available. + + Note that in both cases the return value is falsy. + */ + capi.sqlite3_web_persistent_dir = function(initIfNeeded=true){ + if(undefined !== __persistentDir) return __persistentDir; + else if(!initIfNeeded) return; + // If we have no OPFS, there is no persistent dir + if(!self.FileSystemHandle || !self.FileSystemDirectoryHandle + || !self.FileSystemFileHandle){ + return __persistentDir = ""; + } + try{ + if(0===this.wasm.xCall('sqlite3_wasm_init_opfs')){ + /** OPFS does not support locking and will trigger errors if + we try to lock. We don't _really_ want to + _unconditionally_ install a non-locking sqlite3 VFS as the + default, but we do so here for simplicy's sake for the + time being. That said: locking is a no-op on all of the + current WASM storage, so this isn't (currently) as bad as + it may initially seem. */ + const pVfs = this.sqlite3_vfs_find("unix-none"); + if(pVfs){ + this.sqlite3_vfs_register(pVfs,1); + //warn("Installed 'unix-none' as the default sqlite3 VFS."); + } + return __persistentDir = + "/persistent" /* name is hard-coded in sqlite3_wasm_init_opfs()!*/; + }else{ + return __persistentDir = ""; + } + }catch(e){ + // sqlite3_wasm_init_opfs() is not available + return __persistentDir = ""; + } + }.bind(capi); + /* The remainder of the API will be set up in later steps. */ return { capi, diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index 30a9dafeb8..487baecf10 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -430,7 +430,7 @@ int sqlite3_wasm_vfs_unlink(const char * zName){ return rc; } -#ifdef __EMSCRIPTEN__ +#if defined(__EMSCRIPTEN__) && defined(SQLITE_WASM_OPFS) #include #include /* @@ -443,17 +443,34 @@ int sqlite3_wasm_vfs_unlink(const char * zName){ ** WASMFS backend impl for OPFS. On success, subsequent calls are ** no-ops. ** -** Returns 0 on success, SQLITE_NOMEM if intantiation of the backend -** object fails. +** Returns 0 on success, SQLITE_NOMEM if instantiation of the backend +** object fails, SQLITE_IOERR if mkdir() of the "/persistent" dir in +** the virtual FS fails. In builds compiled without SQLITE_WASM_OPFS +** defined, SQLITE_NOTFOUND is returned without side effects. */ int sqlite3_wasm_init_opfs(void){ static backend_t pOpfs = 0; + static const char * zDir = "/persistent"; if( !pOpfs ){ pOpfs = wasmfs_create_opfs_backend(); if( pOpfs ){ - emscripten_console_log("Created OPFS WASMFS backend."); + emscripten_console_log("Created WASMFS OPFS backend."); } } + /** It's not enough to instantiate the backend. We have to create a + mountpoint in the VFS and attach the backend to it. */ + if( pOpfs && 0!=access(zDir, F_OK) ){ + /* mkdir() simply hangs when called from fiddle app. Cause is + not yet determined but the hypothesis is an init-order + issue. */ + const int rc = wasmfs_create_directory(zDir, 0777, pOpfs); + emscripten_console_log(rc ? "OPFS mkdir failed." : "OPFS mkdir ok."); + if(rc) return SQLITE_IOERR; + } return pOpfs ? 0 : SQLITE_NOMEM; } -#endif /* __EMSCRIPTEN__ */ +#else +int sqlite3_wasm_init_opfs(void){ + return SQLITE_NOTFOUND; +} +#endif /* __EMSCRIPTEN__ && SQLITE_WASM_OPFS */ diff --git a/ext/wasm/testing1.js b/ext/wasm/testing1.js index a733156e7a..779e0bd72f 100644 --- a/ext/wasm/testing1.js +++ b/ext/wasm/testing1.js @@ -19,7 +19,8 @@ const toss = function(...args){throw new Error(args.join(' '))}; const debug = console.debug.bind(console); const eOutput = document.querySelector('#test-output'); - const log = console.log.bind(console) + const log = console.log.bind(console), + warn = console.warn.bind(console); const logHtml = function(...args){ log.apply(this, args); const ln = document.createElement('div'); @@ -1012,7 +1013,7 @@ wasm = capi.wasm; log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); log("Build options:",wasm.compileOptionUsed()); - + capi.sqlite3_web_persistent_dir()/*will install OPFS if available, plus a and non-locking VFS*/; if(1){ /* Let's grab those last few lines of test coverage for sqlite3-api.js... */ diff --git a/ext/wasm/testing2.js b/ext/wasm/testing2.js index 3a279513f8..f6792955f8 100644 --- a/ext/wasm/testing2.js +++ b/ext/wasm/testing2.js @@ -20,7 +20,7 @@ id: undefined }; const eOutput = document.querySelector('#test-output'); - const log = console.log.bind(console) + const log = console.log.bind(console); const logHtml = function(cssClass,...args){ log.apply(this, args); const ln = document.createElement('div'); diff --git a/manifest b/manifest index 73a423621e..f1d2fe4641 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sOPFS\sfrom\sthe\sfiddle\sbuild\sfor\sthe\stime\sbeing\s-\swill\sre-enable\sonce\sthe\sbreakage\sis\sfigured\sout\svia\stesting\swith\sthe\score\sAPI. -D 2022-08-13T13:51:56.967 +C Cleanups\sin\sthe\swasmfs/opfs\sintegration\sbut\sdisable\sit\sin\sorder\sto\sget\sthe\sbuild\sinto\sa\sknown-working\sstate\sbefore\scontinuing\swith\sexperimentation. +D 2022-08-13T13:56:00.886 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile c354ad5ce3ecec80c28ed4314827adc10cbbc0ef47e028b0cfbdf336c4a98fa4 +F ext/wasm/GNUmakefile 15ee5d1e182bcee23f0a5fa8062697f4135606c642efe600fa2d5e794ad71588 F ext/wasm/README.md 4b00ae7c7d93c4591251245f0996a319e2651361013c98d2efb0b026771b7331 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -485,10 +485,10 @@ F ext/wasm/api/sqlite3-api-cleanup.js 149fd63a0400cd1d69548887ffde2ed89c13283384 F ext/wasm/api/sqlite3-api-glue.js 82c09f49c69984009ba5af2b628e67cc26c5dd203d383cd3091d40dab4e6514b F ext/wasm/api/sqlite3-api-oo1.js a3469bbb217b9787ba9aa6216423ec55cf9457fecefb9698e433d0e1cc4cc918 F ext/wasm/api/sqlite3-api-opfs.js c93cdd14f81a26b3a64990515ee05c7e29827fbc8fba4e4c2fef3a37a984db89 -F ext/wasm/api/sqlite3-api-prologue.js 0fb0703d2d8ac89fa2d4dd8f9726b0ea226b8708ac34e5b482df046e147de0eb +F ext/wasm/api/sqlite3-api-prologue.js c0f335bf8b44071da0204b8fa95ce78fd737033b155e7bcfdaee6ae64600802f F ext/wasm/api/sqlite3-api-worker.js 1124f404ecdf3c14d9f829425cef778cd683911a9883f0809a463c3c7773c9fd F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 -F ext/wasm/api/sqlite3-wasm.c 0b3f56078f3e3806fb6dfbc756198d1634d3bfdbf3677c77702cf453c4ed336a +F ext/wasm/api/sqlite3-wasm.c 0e78035045e3328fb050ec9580c6bfb714c756a1d3b917259e58baf9b0332c98 F ext/wasm/api/sqlite3-worker.js 1325ca8d40129a82531902a3a077b795db2eeaee81746e5a0c811a04b415fa7f F ext/wasm/common/SqliteTestUtil.js e41a1406f18da9224523fad0c48885caf995b56956a5b9852909c0989e687e90 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f @@ -503,9 +503,9 @@ F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356a F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19 F ext/wasm/testing1.html 0bf3ff224628c1f1e3ed22a2dc1837c6c73722ad8c0ad9c8e6fb9e6047667231 -F ext/wasm/testing1.js cba7134901a965743fa9289d82447ab71de4690b1ee5d06f6cb83e8b569d7943 +F ext/wasm/testing1.js a25069e20d5f8dc548cc98bcf7002cec812084421a1f7f70ffae2c706d1167b2 F ext/wasm/testing2.html 73e5048e666fd6fb28b6e635677a9810e1e139c599ddcf28d687c982134b92b8 -F ext/wasm/testing2.js d37433c601f88ed275712c1cfc92d3fb36c7c22e1ed8c7396fb2359e42238ebc +F ext/wasm/testing2.js 1cd14be666e40da41d7eea5723b1953ce54f1077c199887a73d3d5cfb71dbd05 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -1999,8 +1999,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e37dddc1dd9c0530e4b1c6cb0ca7cba7451caa37734d383c9b47f378d7222242 -R 988d3478699d43ef2ce9b5c2bc8119e6 +P 3bc510a614973eafa60960a99bedb063594a693bdbfd80d7eb480b293b4ab811 +R a2b4eef7c61958031f6931d9df602912 U stephan -Z c96da5568e2699a3b63963443a07c191 +Z 3b291cb3c0f3e97625538cf3bb7fa23a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f61125d960..91690836df 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3bc510a614973eafa60960a99bedb063594a693bdbfd80d7eb480b293b4ab811 \ No newline at end of file +41045be752a5bd7966849638f3ca56f4905308df70f79f2cb6196ca7dce9d525 \ No newline at end of file From 1025227272ac7a16d0869d7cacde54603f5e2148 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 13 Aug 2022 16:11:38 +0000 Subject: [PATCH 007/428] Add scratchpad/test app for WASMFS/OPFS running in the main window thread. Enable WASMFS by default in the library build. FossilOrigin-Name: ae24ac0f7dd9e12a40de0f6ccd61a16f010804da454085f886c217cc600cdba4 --- ext/wasm/GNUmakefile | 21 +++++---- ext/wasm/scratchpad-opfs-main.html | 40 ++++++++++++++++ ext/wasm/scratchpad-opfs-main.js | 73 ++++++++++++++++++++++++++++++ manifest | 14 +++--- manifest.uuid | 2 +- 5 files changed, 133 insertions(+), 17 deletions(-) create mode 100644 ext/wasm/scratchpad-opfs-main.html create mode 100644 ext/wasm/scratchpad-opfs-main.js diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index dad77dba0a..1fc2a57652 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -168,7 +168,6 @@ emcc.cflags := emcc.cflags += -std=c99 -fPIC # -------------^^^^^^^^ we currently need c99 for WASM-specific sqlite3 APIs. emcc.cflags += -I. -I$(dir.top) # $(SQLITE_OPT) -emcc.cflags += -pthread ######################################################################## # emcc flags specific to building the final .js/.wasm file... @@ -183,36 +182,38 @@ emcc.jsflags += -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory # wasmMemory==>for -sIM emcc.jsflags += -sUSE_CLOSURE_COMPILER=0 emcc.jsflags += -sIMPORTED_MEMORY emcc.environment := -sENVIRONMENT=web -ifeq (0,1) +ENABLE_WASMFS := 1 +ifneq (0,$(ENABLE_WASMFS)) + emcc.cflags += -pthread emcc.jsflags += -pthread -sWASMFS -sPTHREAD_POOL_SIZE=2 emcc.environment := $(emcc.environment),worker +else + emcc.jsflags += -sALLOW_MEMORY_GROWTH + # emcc: warning: USE_PTHREADS + ALLOW_MEMORY_GROWTH may run non-wasm code + # slowly, see https://github.com/WebAssembly/design/issues/1271 + # [-Wpthreads-mem-growth] endif emcc.jsflags += $(emcc.environment) #emcc.jsflags += -sINITIAL_MEMORY=13107200 #emcc.jsflags += -sTOTAL_STACK=4194304 emcc.jsflags += -sEXPORT_NAME=sqlite3InitModule emcc.jsflags += -sGLOBAL_BASE=4096 # HYPOTHETICALLY keep func table indexes from overlapping w/ heap addr. -emcc.jsflags +=--post-js=$(post-js.js) +emcc.jsflags += --post-js=$(post-js.js) #emcc.jsflags += -sSTRICT # fails due to missing __syscall_...() #emcc.jsflags += -sALLOW_UNIMPLEMENTED_SYSCALLS #emcc.jsflags += -sFILESYSTEM=0 # only for experimentation. sqlite3 needs the FS API #emcc.jsflags += -sABORTING_MALLOC -emcc.jsflags += -sALLOW_MEMORY_GROWTH emcc.jsflags += -sALLOW_TABLE_GROWTH emcc.jsflags += -Wno-limited-postlink-optimizations # ^^^^^ it likes to warn when we have "limited optimizations" via the -g3 flag. -#emcc.jsflags += -sMALLOC=emmalloc -#emcc.jsflags += -sMALLOC=dlmalloc # a good 8k larger than emmalloc #emcc.jsflags += -sSTANDALONE_WASM # causes OOM errors, not sure why -#emcc.jsflags += --import=foo_bar -#emcc.jsflags += --no-gc-sections # https://lld.llvm.org/WebAssembly.html emcc.jsflags += -sERROR_ON_UNDEFINED_SYMBOLS=0 emcc.jsflags += -sLLD_REPORT_UNDEFINED #emcc.jsflags += --allow-undefined emcc.jsflags += --import-undefined #emcc.jsflags += --unresolved-symbols=import-dynamic --experimental-pic -#emcc.jsflags += --experimental-pic --unresolved-symbols=ingore-all --import-undefined +#emcc.jsflags += --experimental-pic --unresolved-symbols=ingore-all --import-undefined #emcc.jsflags += --unresolved-symbols=ignore-all enable_bigint ?= 1 ifneq (0,$(enable_bigint)) @@ -228,7 +229,7 @@ emcc.jsflags += -sMEMORY64=0 # new Uint8Array(heapWrappers().HEAP8U.buffer, ptr, n) # # because ptr is now a BigInt, so is invalid for passing to arguments -# which have strict must-be-a-number requirements. +# which have strict must-be-a-Number requirements. ######################################################################## diff --git a/ext/wasm/scratchpad-opfs-main.html b/ext/wasm/scratchpad-opfs-main.html new file mode 100644 index 0000000000..36ae55c147 --- /dev/null +++ b/ext/wasm/scratchpad-opfs-main.html @@ -0,0 +1,40 @@ + + + + + + + + + sqlite3 WASMFS/OPFS Main-thread Scratchpad + + +
sqlite3 WASMFS/OPFS Main-thread Scratchpad
+ +
+
+
Initializing app...
+
+ 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. +
+
+
Downloading...
+
+ +
+

Scratchpad/test app for the WASMF/OPFS integration in the + main window thread. This page requires that the sqlite3 API have + been built with WASMFS support. If OPFS support is available then + it "should" persist a database across reloads (watch the dev console + output), otherwise it will not. +

+

All stuff on this page happens in the dev console.

+
+
+ + + + + diff --git a/ext/wasm/scratchpad-opfs-main.js b/ext/wasm/scratchpad-opfs-main.js new file mode 100644 index 0000000000..f0e38fc86e --- /dev/null +++ b/ext/wasm/scratchpad-opfs-main.js @@ -0,0 +1,73 @@ +/* + 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. This file must be run in + main JS thread and sqlite3.js must have been loaded before it. +*/ +'use strict'; +(function(){ + const toss = function(...args){throw new Error(args.join(' '))}; + const log = console.log.bind(console), + warn = console.warn.bind(console), + error = console.error.bind(console); + + const stdout = log; + const stderr = error; + + const test1 = function(db){ + db.execMulti("create table if not exists t(a);") + .callInTransaction(function(db){ + db.prepare("insert into t(a) values(?)") + .bind(new Date().getTime()) + .stepFinalize(); + stdout("Number of values in table t:", + db.selectValue("select count(*) from t")); + }); + }; + + const runTests = function(Module){ + //stdout("Module",Module); + self._MODULE = Module /* this is only to facilitate testing from the console */; + const sqlite3 = Module.sqlite3, + capi = sqlite3.capi, + oo = sqlite3.oo1, + wasm = capi.wasm; + stdout("Loaded sqlite3:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); + const persistentDir = capi.sqlite3_web_persistent_dir(); + if(persistentDir){ + stdout("Persistent storage dir:",persistentDir); + }else{ + stderr("No persistent storage available."); + } + const startTime = performance.now(); + let db; + try { + db = new oo.DB(persistentDir+'/foo.db'); + stdout("DB filename:",db.filename,db.fileName()); + const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', + banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'; + [ + test1 + ].forEach((f)=>{ + const n = performance.now(); + stdout(banner1,"Running",f.name+"()..."); + f(db, sqlite3, Module); + stdout(banner2,f.name+"() took ",(performance.now() - n),"ms"); + }); + }finally{ + if(db) db.close(); + } + stdout("Total test time:",(performance.now() - startTime),"ms"); + }; + + sqlite3InitModule(self.sqlite3TestModule).then(runTests); +})(); diff --git a/manifest b/manifest index f1d2fe4641..a573cd64fc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Cleanups\sin\sthe\swasmfs/opfs\sintegration\sbut\sdisable\sit\sin\sorder\sto\sget\sthe\sbuild\sinto\sa\sknown-working\sstate\sbefore\scontinuing\swith\sexperimentation. -D 2022-08-13T13:56:00.886 +C Add\sscratchpad/test\sapp\sfor\sWASMFS/OPFS\srunning\sin\sthe\smain\swindow\sthread.\sEnable\sWASMFS\sby\sdefault\sin\sthe\slibrary\sbuild. +D 2022-08-13T16:11:38.873 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 15ee5d1e182bcee23f0a5fa8062697f4135606c642efe600fa2d5e794ad71588 +F ext/wasm/GNUmakefile 1bcfcde973bfbf1ea59acc551728aaf0fc74fcb52ddcd219a9b0f93e42c744f0 F ext/wasm/README.md 4b00ae7c7d93c4591251245f0996a319e2651361013c98d2efb0b026771b7331 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -502,6 +502,8 @@ F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab26 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19 +F ext/wasm/scratchpad-opfs-main.html 079b6ec0b3a6c35c9ac92e639ede1b253b901c52ec6a793e5411babb708ace40 +F ext/wasm/scratchpad-opfs-main.js 55ac5a0841d6436ac2990a4c26fea7f7fb98b0cfbb02ac169dc91f3c9ed5303d F ext/wasm/testing1.html 0bf3ff224628c1f1e3ed22a2dc1837c6c73722ad8c0ad9c8e6fb9e6047667231 F ext/wasm/testing1.js a25069e20d5f8dc548cc98bcf7002cec812084421a1f7f70ffae2c706d1167b2 F ext/wasm/testing2.html 73e5048e666fd6fb28b6e635677a9810e1e139c599ddcf28d687c982134b92b8 @@ -1999,8 +2001,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 3bc510a614973eafa60960a99bedb063594a693bdbfd80d7eb480b293b4ab811 -R a2b4eef7c61958031f6931d9df602912 +P 41045be752a5bd7966849638f3ca56f4905308df70f79f2cb6196ca7dce9d525 +R 767e1ce7730172518cd5acb6e01d575b U stephan -Z 3b291cb3c0f3e97625538cf3bb7fa23a +Z 6b8d7c0e41c798b0afb745ee9b8f505a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 91690836df..7336ee31ab 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -41045be752a5bd7966849638f3ca56f4905308df70f79f2cb6196ca7dce9d525 \ No newline at end of file +ae24ac0f7dd9e12a40de0f6ccd61a16f010804da454085f886c217cc600cdba4 \ No newline at end of file From a7234901b22f95f6079249f0acdce97646760a28 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 13 Aug 2022 16:36:06 +0000 Subject: [PATCH 008/428] wasmfs: use unix-none VFS by default to avoid locking errors in non-OPFS mode. FossilOrigin-Name: 75561dea1a1afe9cb0a7d58dd82fa519e51cf42e330922cfd8e9ccdf6db4dc0f --- ext/wasm/GNUmakefile | 3 ++- ext/wasm/scratchpad-opfs-main.js | 2 +- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 1fc2a57652..1b058b342b 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -182,9 +182,10 @@ emcc.jsflags += -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory # wasmMemory==>for -sIM emcc.jsflags += -sUSE_CLOSURE_COMPILER=0 emcc.jsflags += -sIMPORTED_MEMORY emcc.environment := -sENVIRONMENT=web -ENABLE_WASMFS := 1 +ENABLE_WASMFS ?= 1 ifneq (0,$(ENABLE_WASMFS)) emcc.cflags += -pthread + emcc.cflags += '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' emcc.jsflags += -pthread -sWASMFS -sPTHREAD_POOL_SIZE=2 emcc.environment := $(emcc.environment),worker else diff --git a/ext/wasm/scratchpad-opfs-main.js b/ext/wasm/scratchpad-opfs-main.js index f0e38fc86e..8e9d123584 100644 --- a/ext/wasm/scratchpad-opfs-main.js +++ b/ext/wasm/scratchpad-opfs-main.js @@ -24,7 +24,7 @@ const stderr = error; const test1 = function(db){ - db.execMulti("create table if not exists t(a);") + db.exec("create table if not exists t(a);") .callInTransaction(function(db){ db.prepare("insert into t(a) values(?)") .bind(new Date().getTime()) diff --git a/manifest b/manifest index a573cd64fc..8a59e270a3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sscratchpad/test\sapp\sfor\sWASMFS/OPFS\srunning\sin\sthe\smain\swindow\sthread.\sEnable\sWASMFS\sby\sdefault\sin\sthe\slibrary\sbuild. -D 2022-08-13T16:11:38.873 +C wasmfs:\suse\sunix-none\sVFS\sby\sdefault\sto\savoid\slocking\serrors\sin\snon-OPFS\smode. +D 2022-08-13T16:36:06.930 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 1bcfcde973bfbf1ea59acc551728aaf0fc74fcb52ddcd219a9b0f93e42c744f0 +F ext/wasm/GNUmakefile 5c9b08139739819fc6223954c1b832d90398e9115789189ab39e5fbc6c764269 F ext/wasm/README.md 4b00ae7c7d93c4591251245f0996a319e2651361013c98d2efb0b026771b7331 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -503,7 +503,7 @@ F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356a F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19 F ext/wasm/scratchpad-opfs-main.html 079b6ec0b3a6c35c9ac92e639ede1b253b901c52ec6a793e5411babb708ace40 -F ext/wasm/scratchpad-opfs-main.js 55ac5a0841d6436ac2990a4c26fea7f7fb98b0cfbb02ac169dc91f3c9ed5303d +F ext/wasm/scratchpad-opfs-main.js a819ed26047c5539630cea59add6a5082ba04cdf82da2df2e0707d4d69af6cb1 F ext/wasm/testing1.html 0bf3ff224628c1f1e3ed22a2dc1837c6c73722ad8c0ad9c8e6fb9e6047667231 F ext/wasm/testing1.js a25069e20d5f8dc548cc98bcf7002cec812084421a1f7f70ffae2c706d1167b2 F ext/wasm/testing2.html 73e5048e666fd6fb28b6e635677a9810e1e139c599ddcf28d687c982134b92b8 @@ -2001,8 +2001,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 41045be752a5bd7966849638f3ca56f4905308df70f79f2cb6196ca7dce9d525 -R 767e1ce7730172518cd5acb6e01d575b +P ae24ac0f7dd9e12a40de0f6ccd61a16f010804da454085f886c217cc600cdba4 +R 04c2d08427afe72dca8dfa59a558b201 U stephan -Z 6b8d7c0e41c798b0afb745ee9b8f505a +Z 16beafa09646f238ac043a3e648d875d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7336ee31ab..280b9edc80 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ae24ac0f7dd9e12a40de0f6ccd61a16f010804da454085f886c217cc600cdba4 \ No newline at end of file +75561dea1a1afe9cb0a7d58dd82fa519e51cf42e330922cfd8e9ccdf6db4dc0f \ No newline at end of file From 0761780bc48753ed85c7d3513535496b36a015a9 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 13 Aug 2022 17:13:16 +0000 Subject: [PATCH 009/428] Add worker-style variant of the tests added in [ae24ac0f7dd9], but building this with wasmfs causes them to throw inexplicable exceptions from the Emscripten glue (without wasmfs it builds and runs fine, but storage is not persistent). FossilOrigin-Name: 6401595e59179c5c0f6e51c5362cf4391787e7a55b9c6ca655746e30d3251f2b --- ext/wasm/api/scratchpad-opfs-worker.js | 86 ++++++++++++++++++++++++++ ext/wasm/scratchpad-opfs-worker.html | 39 ++++++++++++ ext/wasm/scratchpad-opfs-worker.js | 33 ++++++++++ manifest | 13 ++-- manifest.uuid | 2 +- 5 files changed, 167 insertions(+), 6 deletions(-) create mode 100644 ext/wasm/api/scratchpad-opfs-worker.js create mode 100644 ext/wasm/scratchpad-opfs-worker.html create mode 100644 ext/wasm/scratchpad-opfs-worker.js diff --git a/ext/wasm/api/scratchpad-opfs-worker.js b/ext/wasm/api/scratchpad-opfs-worker.js new file mode 100644 index 0000000000..5bc63c7966 --- /dev/null +++ b/ext/wasm/api/scratchpad-opfs-worker.js @@ -0,0 +1,86 @@ +/* + 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. + + *********************************************************************** + + An experiment for wasmfs/opfs. This file MUST be in the same dir as + the sqlite3.js emscripten module or that module won't be able to + resolve the relative URIs (importScript()'s relative URI handling + is, quite frankly, broken). +*/ +'use strict'; +(function(){ + const toss = function(...args){throw new Error(args.join(' '))}; + importScripts('sqlite3.js'); + + /** + 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 = console.log.bind(console); + const stderr = function(...args){wMsg('stderr', args);}; + + const test1 = function(db){ + db.execMulti("create table if not exists t(a);") + .callInTransaction(function(db){ + db.prepare("insert into t(a) values(?)") + .bind(new Date().getTime()) + .stepFinalize(); + stdout("Number of values in table t:", + db.selectValue("select count(*) from t")); + }); + }; + + const runTests = function(Module){ + //stdout("Module",Module); + self._MODULE = Module /* this is only to facilitate testing from the console */; + const sqlite3 = Module.sqlite3, + capi = sqlite3.capi, + oo = sqlite3.oo1, + wasm = capi.wasm; + stdout("Loaded sqlite3:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); + const persistentDir = capi.sqlite3_web_persistent_dir(); + if(persistentDir){ + stderr("Persistent storage dir:",persistentDir); + }else{ + stderr("No persistent storage available."); + } + const startTime = performance.now(); + let db; + try { + db = new oo.DB(persistentDir+'/foo.db'); + stdout("DB filename:",db.filename,db.fileName()); + const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', + banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'; + [ + test1 + ].forEach((f)=>{ + const n = performance.now(); + stdout(banner1,"Running",f.name+"()..."); + f(db, sqlite3, Module); + stdout(banner2,f.name+"() took ",(performance.now() - n),"ms"); + }); + }finally{ + if(db) db.close(); + } + stdout("Total test time:",(performance.now() - startTime),"ms"); + }; + + sqlite3InitModule(self.sqlite3TestModule).then(runTests); +})(); diff --git a/ext/wasm/scratchpad-opfs-worker.html b/ext/wasm/scratchpad-opfs-worker.html new file mode 100644 index 0000000000..2f3e5b1de6 --- /dev/null +++ b/ext/wasm/scratchpad-opfs-worker.html @@ -0,0 +1,39 @@ + + + + + + + + + sqlite3 WASMFS/OPFS Worker-thread Scratchpad + + +
sqlite3 WASMFS/OPFS Worker-thread Scratchpad
+ + +

This test is known, as of 2022-08-13, to not work.

+

Scratchpad/test app for the WASMF/OPFS integration in a + WORKER thread. This page requires that the sqlite3 API have + been built with WASMFS support. If OPFS support is available then + it "should" persist a database across reloads (watch the dev console + output), otherwise it will not. +

+

All stuff on this page happens in the dev console.

+
+
+ + + diff --git a/ext/wasm/scratchpad-opfs-worker.js b/ext/wasm/scratchpad-opfs-worker.js new file mode 100644 index 0000000000..5590a5a44a --- /dev/null +++ b/ext/wasm/scratchpad-opfs-worker.js @@ -0,0 +1,33 @@ +/* + 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. This file must be run in + main JS thread and sqlite3.js must have been loaded before it. +*/ +'use strict'; +(function(){ + const toss = function(...args){throw new Error(args.join(' '))}; + const log = console.log.bind(console), + warn = console.warn.bind(console), + error = console.error.bind(console); + + const W = new Worker("api/scratchpad-opfs-worker.js"); + self.onmessage = function(ev){ + ev = ev.data; + const d = ev.data; + switch(ev.type){ + case 'stdout': log(d); break; + case 'stderr': error(d); break; + default: warn("Unhandled message type:",ev); break; + } + }; +})(); diff --git a/manifest b/manifest index 8a59e270a3..1949713dc1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C wasmfs:\suse\sunix-none\sVFS\sby\sdefault\sto\savoid\slocking\serrors\sin\snon-OPFS\smode. -D 2022-08-13T16:36:06.930 +C Add\sworker-style\svariant\sof\sthe\stests\sadded\sin\s[ae24ac0f7dd9],\sbut\sbuilding\sthis\swith\swasmfs\scauses\sthem\sto\sthrow\sinexplicable\sexceptions\sfrom\sthe\sEmscripten\sglue\s(without\swasmfs\sit\sbuilds\sand\sruns\sfine,\sbut\sstorage\sis\snot\spersistent). +D 2022-08-13T17:13:16.407 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -481,6 +481,7 @@ F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de F ext/wasm/api/README.md b6d0fb64bfdf7bf9ce6938ea4104228f6f5bbef600f5d910b2f8c8694195988c F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b +F ext/wasm/api/scratchpad-opfs-worker.js ccafcbc548f8ead91e5151c26ea7fb4ded705fd6b65c4557c6c2bcd6a8922d85 F ext/wasm/api/sqlite3-api-cleanup.js 149fd63a0400cd1d69548887ffde2ed89c13283384a63c2e9fcfc695e38a9e11 F ext/wasm/api/sqlite3-api-glue.js 82c09f49c69984009ba5af2b628e67cc26c5dd203d383cd3091d40dab4e6514b F ext/wasm/api/sqlite3-api-oo1.js a3469bbb217b9787ba9aa6216423ec55cf9457fecefb9698e433d0e1cc4cc918 @@ -504,6 +505,8 @@ F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5 F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19 F ext/wasm/scratchpad-opfs-main.html 079b6ec0b3a6c35c9ac92e639ede1b253b901c52ec6a793e5411babb708ace40 F ext/wasm/scratchpad-opfs-main.js a819ed26047c5539630cea59add6a5082ba04cdf82da2df2e0707d4d69af6cb1 +F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 +F ext/wasm/scratchpad-opfs-worker.js c42a10db96e1c313175e2817766789aa4ce175c9574532e76082e69ba7d08f05 F ext/wasm/testing1.html 0bf3ff224628c1f1e3ed22a2dc1837c6c73722ad8c0ad9c8e6fb9e6047667231 F ext/wasm/testing1.js a25069e20d5f8dc548cc98bcf7002cec812084421a1f7f70ffae2c706d1167b2 F ext/wasm/testing2.html 73e5048e666fd6fb28b6e635677a9810e1e139c599ddcf28d687c982134b92b8 @@ -2001,8 +2004,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P ae24ac0f7dd9e12a40de0f6ccd61a16f010804da454085f886c217cc600cdba4 -R 04c2d08427afe72dca8dfa59a558b201 +P 75561dea1a1afe9cb0a7d58dd82fa519e51cf42e330922cfd8e9ccdf6db4dc0f +R 2cbf5edf37d119630242142baab50d57 U stephan -Z 16beafa09646f238ac043a3e648d875d +Z 4aeebb4853d4fddddabda504cb5e02f4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 280b9edc80..28b38af321 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -75561dea1a1afe9cb0a7d58dd82fa519e51cf42e330922cfd8e9ccdf6db4dc0f \ No newline at end of file +6401595e59179c5c0f6e51c5362cf4391787e7a55b9c6ca655746e30d3251f2b \ No newline at end of file From ab7de45084bab63f506a9a5750414190ea30ebd7 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 16 Aug 2022 16:06:12 +0000 Subject: [PATCH 010/428] wasm: disable shared cache mode by default. Experimentally move wasm-build generated files up one dir to rule the extra dir out as a problem for Emscripten-related worker-loading failures. FossilOrigin-Name: e38d00c2b82d7e51ec04cd739514f255edde679b8ddab31fa944b3517d45c3ea --- Makefile.in | 1 + ext/wasm/GNUmakefile | 18 ++++++++++++------ manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/Makefile.in b/Makefile.in index c9de1403f4..56ab4c88a1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1565,6 +1565,7 @@ emcc_flags = $(emcc_opt) \ -DSQLITE_THREADSAFE=0 \ -DSQLITE_OMIT_UTF16 \ -DSQLITE_OMIT_DEPRECATED \ + -DSQLITE_OMIT_SHARED_CACHE \ $(emcc_flags_opfs) $(fiddle_module_js): Makefile $(sqlite3_wasm_c) shell.c \ $(wasm_dir)/EXPORTED_RUNTIME_METHODS.fiddle \ diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 1b058b342b..9522ab72a5 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -45,6 +45,7 @@ SQLITE_OPT = \ -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_OMIT_UTF16 \ + -DSQLITE_OMIT_SHARED_CACHE \ -DSQLITE_THREADSAFE=0 #SQLITE_OPT += -DSQLITE_ENABLE_MEMSYS5 $(dir.top)/sqlite3.c: @@ -131,7 +132,7 @@ sqlite3-api.jses := \ #sqlite3-api.jses += $(dir.api)/sqlite3-api-opfs.js sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js -sqlite3-api.js := $(dir.api)/sqlite3-api.js +sqlite3-api.js := sqlite3-api.js CLEAN_FILES += $(sqlite3-api.js) $(sqlite3-api.js): $(sqlite3-api.jses) $(MAKEFILE) @echo "Making $@..." @@ -141,7 +142,7 @@ $(sqlite3-api.js): $(sqlite3-api.jses) $(MAKEFILE) echo "/* END FILE: $$i */"; \ done > $@ -post-js.js := $(dir.api)/post-js.js +post-js.js := post-js.js CLEAN_FILES += $(post-js.js) post-jses := \ $(dir.api)/post-js-header.js \ @@ -185,8 +186,8 @@ emcc.environment := -sENVIRONMENT=web ENABLE_WASMFS ?= 1 ifneq (0,$(ENABLE_WASMFS)) emcc.cflags += -pthread - emcc.cflags += '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' emcc.jsflags += -pthread -sWASMFS -sPTHREAD_POOL_SIZE=2 + emcc.cflags += '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' emcc.environment := $(emcc.environment),worker else emcc.jsflags += -sALLOW_MEMORY_GROWTH @@ -234,8 +235,13 @@ emcc.jsflags += -sMEMORY64=0 ######################################################################## -sqlite3.js := $(dir.api)/sqlite3.js -sqlite3.wasm := $(dir.api)/sqlite3.wasm +######################################################################## +# Maintenance reminder: the output .js and .wasm files of emcc must be +# in _this_ dir, rather than a subdir, or else parts of the generated +# code get confused and cannot load property (namely, the +# sqlite3.worker.js generated in conjunction with -sWASMFS). +sqlite3.js := sqlite3.js +sqlite3.wasm := sqlite3.wasm $(dir.api)/sqlite3-wasm.o: emcc.cflags += $(SQLITE_OPT) $(dir.api)/sqlite3-wasm.o: $(dir.top)/sqlite3.c $(dir.api)/wasm_util.o: emcc.cflags += $(SQLITE_OPT) @@ -261,7 +267,7 @@ $(sqlite3.js): $(sqlite3.js): $(MAKEFILE) $(sqlite3.wasm.obj) \ EXPORTED_FUNCTIONS.api \ $(post-js.js) - $(emcc.bin) -o $@ $(emcc_opt) $(emcc.flags) $(emcc.jsflags) $(sqlite3.wasm.obj) + $(emcc.bin) -o $(sqlite3.js) $(emcc_opt) $(emcc.flags) $(emcc.jsflags) $(sqlite3.wasm.obj) chmod -x $(sqlite3.wasm) ifneq (,$(wasm-strip)) $(wasm-strip) $(sqlite3.wasm) diff --git a/manifest b/manifest index 1949713dc1..9f7ff290a0 100644 --- a/manifest +++ b/manifest @@ -1,9 +1,9 @@ -C Add\sworker-style\svariant\sof\sthe\stests\sadded\sin\s[ae24ac0f7dd9],\sbut\sbuilding\sthis\swith\swasmfs\scauses\sthem\sto\sthrow\sinexplicable\sexceptions\sfrom\sthe\sEmscripten\sglue\s(without\swasmfs\sit\sbuilds\sand\sruns\sfine,\sbut\sstorage\sis\snot\spersistent). -D 2022-08-13T17:13:16.407 +C wasm:\sdisable\sshared\scache\smode\sby\sdefault.\sExperimentally\smove\swasm-build\sgenerated\sfiles\sup\sone\sdir\sto\srule\sthe\sextra\sdir\sout\sas\sa\sproblem\sfor\sEmscripten-related\sworker-loading\sfailures. +D 2022-08-16T16:06:12.337 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in 419791c8e6f5fda6bf33977680e8f3bf782f5f6ec56b5c22567912c7f8fef353 +F Makefile.in 7d19258e83981c69dcdc63c46dfa83de907fc1c1c0472a6dc0d31b2fae376764 F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 F Makefile.msc d547a2fdba38a1c6cd1954977d0b0cc017f5f8fbfbc65287bf8d335808938016 F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 5c9b08139739819fc6223954c1b832d90398e9115789189ab39e5fbc6c764269 +F ext/wasm/GNUmakefile e3f2a8ce484eea0f087b31f5580b83c136fb6237da26f33f2c1d69f0f31ab94b F ext/wasm/README.md 4b00ae7c7d93c4591251245f0996a319e2651361013c98d2efb0b026771b7331 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -2004,8 +2004,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 75561dea1a1afe9cb0a7d58dd82fa519e51cf42e330922cfd8e9ccdf6db4dc0f -R 2cbf5edf37d119630242142baab50d57 +P 6401595e59179c5c0f6e51c5362cf4391787e7a55b9c6ca655746e30d3251f2b +R 658c31cfb5b14194f7082d626102688f U stephan -Z 4aeebb4853d4fddddabda504cb5e02f4 +Z ece080cb3d48a7dbb6a6c937a212c0a6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 28b38af321..4f1a6b33e5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6401595e59179c5c0f6e51c5362cf4391787e7a55b9c6ca655746e30d3251f2b \ No newline at end of file +e38d00c2b82d7e51ec04cd739514f255edde679b8ddab31fa944b3517d45c3ea \ No newline at end of file From 8ac2d8510efb2ac3aad2a11bb1acbd16ae420cf5 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 16 Aug 2022 16:11:20 +0000 Subject: [PATCH 011/428] wasm: accommodate moving generated sqlite3.js in the previous checkin. FossilOrigin-Name: 41762f9518bb51b8b23ae6507628d6d3256044e1f2aca6e7251dc57722062c42 --- ext/wasm/scratchpad-opfs-main.html | 2 +- ext/wasm/scratchpad-opfs-worker.js | 5 ++--- ...opfs-worker.js => scratchpad-opfs-worker2.js} | 2 +- manifest | 16 ++++++++-------- manifest.uuid | 2 +- 5 files changed, 13 insertions(+), 14 deletions(-) rename ext/wasm/{api/scratchpad-opfs-worker.js => scratchpad-opfs-worker2.js} (96%) diff --git a/ext/wasm/scratchpad-opfs-main.html b/ext/wasm/scratchpad-opfs-main.html index 36ae55c147..8edd15a671 100644 --- a/ext/wasm/scratchpad-opfs-main.html +++ b/ext/wasm/scratchpad-opfs-main.html @@ -33,7 +33,7 @@

All stuff on this page happens in the dev console.


- + diff --git a/ext/wasm/scratchpad-opfs-worker.js b/ext/wasm/scratchpad-opfs-worker.js index 5590a5a44a..debd0245e0 100644 --- a/ext/wasm/scratchpad-opfs-worker.js +++ b/ext/wasm/scratchpad-opfs-worker.js @@ -11,7 +11,7 @@ *********************************************************************** A basic test script for sqlite3-api.js. This file must be run in - main JS thread and sqlite3.js must have been loaded before it. + main JS thread. It will load sqlite3.js in a worker thread. */ 'use strict'; (function(){ @@ -19,8 +19,7 @@ const log = console.log.bind(console), warn = console.warn.bind(console), error = console.error.bind(console); - - const W = new Worker("api/scratchpad-opfs-worker.js"); + const W = new Worker("scratchpad-opfs-worker2.js"); self.onmessage = function(ev){ ev = ev.data; const d = ev.data; diff --git a/ext/wasm/api/scratchpad-opfs-worker.js b/ext/wasm/scratchpad-opfs-worker2.js similarity index 96% rename from ext/wasm/api/scratchpad-opfs-worker.js rename to ext/wasm/scratchpad-opfs-worker2.js index 5bc63c7966..2e7527adc7 100644 --- a/ext/wasm/api/scratchpad-opfs-worker.js +++ b/ext/wasm/scratchpad-opfs-worker2.js @@ -34,7 +34,7 @@ }; const stdout = console.log.bind(console); - const stderr = function(...args){wMsg('stderr', args);}; + const stderr = console.error.bind(console);//function(...args){wMsg('stderr', args);}; const test1 = function(db){ db.execMulti("create table if not exists t(a);") diff --git a/manifest b/manifest index 9f7ff290a0..24170d04b4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C wasm:\sdisable\sshared\scache\smode\sby\sdefault.\sExperimentally\smove\swasm-build\sgenerated\sfiles\sup\sone\sdir\sto\srule\sthe\sextra\sdir\sout\sas\sa\sproblem\sfor\sEmscripten-related\sworker-loading\sfailures. -D 2022-08-16T16:06:12.337 +C wasm:\saccommodate\smoving\sgenerated\ssqlite3.js\sin\sthe\sprevious\scheckin. +D 2022-08-16T16:11:20.050 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -481,7 +481,6 @@ F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de F ext/wasm/api/README.md b6d0fb64bfdf7bf9ce6938ea4104228f6f5bbef600f5d910b2f8c8694195988c F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b -F ext/wasm/api/scratchpad-opfs-worker.js ccafcbc548f8ead91e5151c26ea7fb4ded705fd6b65c4557c6c2bcd6a8922d85 F ext/wasm/api/sqlite3-api-cleanup.js 149fd63a0400cd1d69548887ffde2ed89c13283384a63c2e9fcfc695e38a9e11 F ext/wasm/api/sqlite3-api-glue.js 82c09f49c69984009ba5af2b628e67cc26c5dd203d383cd3091d40dab4e6514b F ext/wasm/api/sqlite3-api-oo1.js a3469bbb217b9787ba9aa6216423ec55cf9457fecefb9698e433d0e1cc4cc918 @@ -503,10 +502,11 @@ F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab26 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19 -F ext/wasm/scratchpad-opfs-main.html 079b6ec0b3a6c35c9ac92e639ede1b253b901c52ec6a793e5411babb708ace40 +F ext/wasm/scratchpad-opfs-main.html 4565cf194e66188190d35f70e82553e2e2d72b9809b73c94ab67b8cfd14d2e0c F ext/wasm/scratchpad-opfs-main.js a819ed26047c5539630cea59add6a5082ba04cdf82da2df2e0707d4d69af6cb1 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 -F ext/wasm/scratchpad-opfs-worker.js c42a10db96e1c313175e2817766789aa4ce175c9574532e76082e69ba7d08f05 +F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 +F ext/wasm/scratchpad-opfs-worker2.js de3ea77548243a638c8426b7e43cc1dbfc511013228ab98436eb102923ed6689 w ext/wasm/api/scratchpad-opfs-worker.js F ext/wasm/testing1.html 0bf3ff224628c1f1e3ed22a2dc1837c6c73722ad8c0ad9c8e6fb9e6047667231 F ext/wasm/testing1.js a25069e20d5f8dc548cc98bcf7002cec812084421a1f7f70ffae2c706d1167b2 F ext/wasm/testing2.html 73e5048e666fd6fb28b6e635677a9810e1e139c599ddcf28d687c982134b92b8 @@ -2004,8 +2004,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 6401595e59179c5c0f6e51c5362cf4391787e7a55b9c6ca655746e30d3251f2b -R 658c31cfb5b14194f7082d626102688f +P e38d00c2b82d7e51ec04cd739514f255edde679b8ddab31fa944b3517d45c3ea +R 9311647d4bd948f09c1d1ad00326a894 U stephan -Z ece080cb3d48a7dbb6a6c937a212c0a6 +Z a8a2ceb6325a77f43310bbc5ef685064 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4f1a6b33e5..f535932813 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e38d00c2b82d7e51ec04cd739514f255edde679b8ddab31fa944b3517d45c3ea \ No newline at end of file +41762f9518bb51b8b23ae6507628d6d3256044e1f2aca6e7251dc57722062c42 \ No newline at end of file From ba851ae72e88960256e1e944e9523f909ea5e500 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 16 Aug 2022 16:16:25 +0000 Subject: [PATCH 012/428] wasm: move another file and update testing1/testing2 to account for [e38d00c2b82d]. Disable wasmfs by default as it breaks the worker-based module loader (reason as yet unknown). FossilOrigin-Name: 6dad5e0573ee866657ee10b43e55b86fc9caac7a66c13bdbd35c3625a4783f14 --- ext/wasm/GNUmakefile | 2 +- ext/wasm/{api => }/sqlite3-worker.js | 0 ext/wasm/testing2.js | 2 +- manifest | 18 +++++++++--------- manifest.uuid | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) rename ext/wasm/{api => }/sqlite3-worker.js (100%) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 9522ab72a5..8a9adb75f5 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -183,7 +183,7 @@ emcc.jsflags += -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory # wasmMemory==>for -sIM emcc.jsflags += -sUSE_CLOSURE_COMPILER=0 emcc.jsflags += -sIMPORTED_MEMORY emcc.environment := -sENVIRONMENT=web -ENABLE_WASMFS ?= 1 +ENABLE_WASMFS ?= 0 ifneq (0,$(ENABLE_WASMFS)) emcc.cflags += -pthread emcc.jsflags += -pthread -sWASMFS -sPTHREAD_POOL_SIZE=2 diff --git a/ext/wasm/api/sqlite3-worker.js b/ext/wasm/sqlite3-worker.js similarity index 100% rename from ext/wasm/api/sqlite3-worker.js rename to ext/wasm/sqlite3-worker.js diff --git a/ext/wasm/testing2.js b/ext/wasm/testing2.js index f6792955f8..bc578bb792 100644 --- a/ext/wasm/testing2.js +++ b/ext/wasm/testing2.js @@ -15,7 +15,7 @@ 'use strict'; (function(){ const T = self.SqliteTestUtil; - const SW = new Worker("api/sqlite3-worker.js"); + const SW = new Worker("sqlite3-worker.js"); const DbState = { id: undefined }; diff --git a/manifest b/manifest index 24170d04b4..c19c285c0d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C wasm:\saccommodate\smoving\sgenerated\ssqlite3.js\sin\sthe\sprevious\scheckin. -D 2022-08-16T16:11:20.050 +C wasm:\smove\sanother\sfile\sand\supdate\stesting1/testing2\sto\saccount\sfor\s[e38d00c2b82d].\sDisable\swasmfs\sby\sdefault\sas\sit\sbreaks\sthe\sworker-based\smodule\sloader\s(reason\sas\syet\sunknown). +D 2022-08-16T16:16:25.326 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile e3f2a8ce484eea0f087b31f5580b83c136fb6237da26f33f2c1d69f0f31ab94b +F ext/wasm/GNUmakefile b5b34ee01efcf7c3d6c53d54a332545f2670e7a5a0c63d6de847d3726e5bc521 F ext/wasm/README.md 4b00ae7c7d93c4591251245f0996a319e2651361013c98d2efb0b026771b7331 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -489,7 +489,6 @@ F ext/wasm/api/sqlite3-api-prologue.js c0f335bf8b44071da0204b8fa95ce78fd737033b1 F ext/wasm/api/sqlite3-api-worker.js 1124f404ecdf3c14d9f829425cef778cd683911a9883f0809a463c3c7773c9fd F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0e78035045e3328fb050ec9580c6bfb714c756a1d3b917259e58baf9b0332c98 -F ext/wasm/api/sqlite3-worker.js 1325ca8d40129a82531902a3a077b795db2eeaee81746e5a0c811a04b415fa7f F ext/wasm/common/SqliteTestUtil.js e41a1406f18da9224523fad0c48885caf995b56956a5b9852909c0989e687e90 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 @@ -506,11 +505,12 @@ F ext/wasm/scratchpad-opfs-main.html 4565cf194e66188190d35f70e82553e2e2d72b9809b F ext/wasm/scratchpad-opfs-main.js a819ed26047c5539630cea59add6a5082ba04cdf82da2df2e0707d4d69af6cb1 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 -F ext/wasm/scratchpad-opfs-worker2.js de3ea77548243a638c8426b7e43cc1dbfc511013228ab98436eb102923ed6689 w ext/wasm/api/scratchpad-opfs-worker.js +F ext/wasm/scratchpad-opfs-worker2.js de3ea77548243a638c8426b7e43cc1dbfc511013228ab98436eb102923ed6689 +F ext/wasm/sqlite3-worker.js 1325ca8d40129a82531902a3a077b795db2eeaee81746e5a0c811a04b415fa7f w ext/wasm/api/sqlite3-worker.js F ext/wasm/testing1.html 0bf3ff224628c1f1e3ed22a2dc1837c6c73722ad8c0ad9c8e6fb9e6047667231 F ext/wasm/testing1.js a25069e20d5f8dc548cc98bcf7002cec812084421a1f7f70ffae2c706d1167b2 F ext/wasm/testing2.html 73e5048e666fd6fb28b6e635677a9810e1e139c599ddcf28d687c982134b92b8 -F ext/wasm/testing2.js 1cd14be666e40da41d7eea5723b1953ce54f1077c199887a73d3d5cfb71dbd05 +F ext/wasm/testing2.js dbb825174878716fab42c340962c0c1b32bfbe26dd16c7b082d30d35f510466c F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2004,8 +2004,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e38d00c2b82d7e51ec04cd739514f255edde679b8ddab31fa944b3517d45c3ea -R 9311647d4bd948f09c1d1ad00326a894 +P 41762f9518bb51b8b23ae6507628d6d3256044e1f2aca6e7251dc57722062c42 +R 8ceb5e9180bc147ed0af56485cecdca6 U stephan -Z a8a2ceb6325a77f43310bbc5ef685064 +Z 7a0a645037fbcb33d5a75b781a0d284d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f535932813..3e6aa5a962 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -41762f9518bb51b8b23ae6507628d6d3256044e1f2aca6e7251dc57722062c42 \ No newline at end of file +6dad5e0573ee866657ee10b43e55b86fc9caac7a66c13bdbd35c3625a4783f14 \ No newline at end of file From e0c582850eedf80d6c03f2cfa1b82b998825464a Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 16 Aug 2022 16:36:19 +0000 Subject: [PATCH 013/428] wasm: add a small demo/presentation app for JS OO API #1 and make a few minor additions to that API. FossilOrigin-Name: d6d79b661a1c6137d4693393e02416da4858d58dc84d144081a48d523655b483 --- ext/wasm/api/sqlite3-api-oo1.js | 126 +++++++++++------- ext/wasm/demo-oo1.html | 34 +++++ ext/wasm/demo-oo1.js | 218 ++++++++++++++++++++++++++++++++ ext/wasm/testing1.html | 2 +- manifest | 18 +-- manifest.uuid | 2 +- 6 files changed, 345 insertions(+), 55 deletions(-) create mode 100644 ext/wasm/demo-oo1.html create mode 100644 ext/wasm/demo-oo1.js diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index b6e63d540a..e54e27ec2b 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -80,35 +80,57 @@ not resolve to real filenames, but "" uses an on-storage temporary database and requires that the VFS support that. - The db is currently opened with a fixed set of flags: - (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | - SQLITE_OPEN_EXRESCODE). This API will change in the future - permit the caller to provide those flags via an additional - argument. + The second argument specifies the open/create mode for the + database. It must be string containing a sequence of letters (in + any order, but case sensitive) specifying the mode: + + - "c" => create if it does not exist, else fail if it does not + exist. Implies the "w" flag. + + - "w" => write. Implies "r": a db cannot be write-only. + + - "r" => read-only if neither "w" nor "c" are provided, else it + is ignored. + + If "w" is not provided, the db is implicitly read-only, noting that + "rc" is meaningless + + Any other letters are currently ignored. The default is + "c". These modes are ignored for the special ":memory:" and "" + names. + + The final argument is currently unimplemented but will eventually + be used to specify an optional sqlite3 VFS implementation name, + as for the final argument to sqlite3_open_v2(). For purposes of passing a DB instance to C-style sqlite3 - functions, its read-only `pointer` property holds its `sqlite3*` - pointer value. That property can also be used to check whether - this DB instance is still open. + functions, the DB object's read-only `pointer` property holds its + `sqlite3*` pointer value. That property can also be used to check + whether this DB instance is still open. */ - const DB = function ctor(fn=':memory:'){ + const DB = function ctor(fn=':memory:', flags='c', vtab="not yet implemented"){ if('string'!==typeof fn){ toss3("Invalid filename for DB constructor."); } + let ptr, oflags = 0; + if( flags.indexOf('c')>=0 ){ + oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; + } + if( flags.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE; + if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY; + oflags |= capi.SQLITE_OPEN_EXRESCODE; const stack = capi.wasm.scopedAllocPush(); - let ptr; try { const ppDb = capi.wasm.scopedAllocPtr() /* output (sqlite3**) arg */; - const rc = capi.sqlite3_open_v2(fn, ppDb, capi.SQLITE_OPEN_READWRITE - | capi.SQLITE_OPEN_CREATE - | capi.SQLITE_OPEN_EXRESCODE, null); + const rc = capi.sqlite3_open_v2(fn, ppDb, oflags, null); ptr = capi.wasm.getMemValue(ppDb, '*'); ctor.checkRc(ptr, rc); - }catch(e){ - if(ptr) capi.sqlite3_close_v2(ptr); + }catch( e ){ + if( ptr ) capi.sqlite3_close_v2(ptr); throw e; + }finally{ + capi.wasm.scopedAllocPop(stack); } - finally{capi.wasm.scopedAllocPop(stack);} this.filename = fn; __ptrMap.set(this, ptr); __stmtMap.set(this, Object.create(null)); @@ -315,11 +337,10 @@ file. If the name is "" or ":memory:", it resolves to false. Note that it is not aware of the peculiarities of URI-style names and a URI-style name for a ":memory:" db will fool it. + Returns false if this db is closed. */ hasFilename: function(){ - const fn = this.filename; - if(!fn || ':memory'===fn) return false; - return true; + return this.filename && ':memory'!==this.filename; }, /** Returns the name of the given 0-based db number, as documented @@ -451,14 +472,14 @@ 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 which has result - _columns_, but only if that statement 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 ... + - .callback = a function which gets called for each row of the + FIRST statement in the SQL which has result _columns_, but only + if that statement has any result _rows_. The callback's "this" + is the options object. 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 = either a string describing what type of argument should be passed as the first argument to the callback or an @@ -479,12 +500,13 @@ the FIRST first statement which has result _columns_ is appended to the array in the format specified for the `rowMode` option, with the exception that the only legal values for - `rowMode` in this case are 'array' or 'object', neither of - which is the default. It is legal to use both `resultRows` and - `callback`, but `resultRows` is likely much simpler to use for - small data sets and can be used over a WebWorker-style message - interface. execMulti() throws if `resultRows` is set and - `rowMode` is 'stmt' (which is the default!). + `rowMode` in this case are 'array', 'object', or an integer, + none of which are the default for `rowMode`. It is legal to use + both `resultRows` and `callback`, but `resultRows` is likely + much simpler to use for small data sets and can be used over a + WebWorker-style message interface. execMulti() throws if + `resultRows` is set and `rowMode` is 'stmt' (which is the + default!). - saveSql = an optional array. If set, the SQL of each executed statement is appended to this array before the @@ -1081,6 +1103,7 @@ delete __stmtMap.get(this.db)[this.pointer]; capi.sqlite3_finalize(this.pointer); __ptrMap.delete(this); + delete this._mayGet; delete this.columnCount; delete this.parameterCount; delete this.db; @@ -1268,6 +1291,27 @@ DB.checkRc(this.db.pointer, rc); } }, + /** + Functions exactly like step() except that... + + 1) On success, it calls this.reset() and returns this object. + 2) On error, it throws and does not call reset(). + + This is intended to simplify constructs like: + + ``` + for(...) { + stmt.bind(...).stepReset(); + } + ``` + + Note that the reset() call makes it illegal to call this.get() + after the step. + */ + stepReset: function(){ + this.step(); + return this.reset(); + }, /** Functions like step() except that it finalizes this statement immediately after stepping unless @@ -1286,17 +1330,9 @@ ``` */ stepFinalize: function(){ - affirmUnlocked(this, 'step()'); - const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer); - switch(rc){ - case capi.SQLITE_DONE: this.finalize(); return false; - case capi.SQLITE_ROW: this.finalize(); return true; - default: - this.finalize(); - console.warn("sqlite3_step() rc=",rc,"SQL =", - capi.sqlite3_sql(this.pointer)); - DB.checkRc(this.db.pointer, rc); - } + const rc = this.step(); + this.finalize(); + return rc; }, /** Fetches the value from the given 0-based column index of @@ -1400,7 +1436,7 @@ default: toss3("Don't know how to translate", "type of result column #"+ndx+"."); } - abort("Not reached."); + toss3("Not reached."); }, /** Equivalent to get(ndx) but coerces the result to an integer. */ diff --git a/ext/wasm/demo-oo1.html b/ext/wasm/demo-oo1.html new file mode 100644 index 0000000000..9b6e8cbfa3 --- /dev/null +++ b/ext/wasm/demo-oo1.html @@ -0,0 +1,34 @@ + + + + + + + + + sqlite3-api OO #1 Demo + + +
sqlite3-api OO #1 Demo
+ +
+
+
Initializing app...
+
+ 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. +
+
+
Downloading...
+
+ +
+
Most stuff on this page happens in the dev console.
+
+
+ + + + + diff --git a/ext/wasm/demo-oo1.js b/ext/wasm/demo-oo1.js new file mode 100644 index 0000000000..2792b50920 --- /dev/null +++ b/ext/wasm/demo-oo1.js @@ -0,0 +1,218 @@ +/* + 2022-08-16 + + 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 demonstration of the SQLite3 OO API #1, shorn of assertions + and the like to improve readability. +*/ +'use strict'; +(function(){ + const T = self.SqliteTestUtil; + const toss = function(...args){throw new Error(args.join(' '))}; + const debug = console.debug.bind(console), + log = console.log.bind(console), + warn = console.warn.bind(console), + error = console.error.bind(console); + + const demo1 = function(sqlite3,EmModule){ + const capi = sqlite3.capi, + oo = sqlite3.oo1, + wasm = capi.wasm; + + // If we have persistent storage, maybe init and mount it: + const dbDir = true + ? "" // this demo works better without persistent storage. + : capi.sqlite3_web_persistent_dir(); + // ^^^ returns name of persistent mount point or "" if we have none + + const db = new oo.DB(dbDir+"/mydb.sqlite3"); + /** + Never(!) rely on garbage collection to clean up DBs and + (especially) statements. Always wrap their lifetimes in + try/finally construct... + */ + try { + log("Create a table..."); + db.exec("CREATE TABLE IF NOT EXISTS t(a,b)"); + //Equivalent: + db.exec({ + sql:"CREATE TABLE IF NOT EXISTS t(a,b)" + // ... numerous other options ... + }); + // SQL can be either a string or a byte array + + log("Insert some data using exec()..."); + let i; + for( i = 1; i <= 5; ++i ){ + db.exec({ + sql: "insert into t(a,b) values (?,?)", + // bind by parameter index... + bind: [i, i*2] + }); + db.exec({ + sql: "insert into t(a,b) values ($a,$b)", + // bind by parameter name... + bind: {$a: i * 3, $b: i * 4} + }); + } + + log("Insert using a prepared statement..."); + let q = db.prepare("insert into t(a,b) values(?,?)"); + try { + for( i = 100; i < 103; ++i ){ + q.bind( [i, i*2] ).step(); + q.reset(); + } + // Equivalent... + for( i = 103; i <= 105; ++i ){ + q.bind(1, i).bind(2, i*2).stepReset(); + } + }finally{ + q.finalize(); + } + + log("Query data with exec() using rowMode 'array'..."); + db.exec({ + sql: "select a from t order by a limit 3", + rowMode: 'array', // 'array', 'object', or 'stmt' (default) + callback: function(row){ + log("row ",++this.counter,"=",row); + }.bind({counter: 0}) + }); + + log("Query data with exec() using rowMode 'object'..."); + db.exec({ + sql: "select a as aa, b as bb from t order by aa limit 3", + rowMode: 'object', + callback: function(row){ + log("row ",++this.counter,"=",row); + }.bind({counter: 0}) + }); + + log("Query data with exec() using rowMode 'stmt'..."); + db.exec({ + sql: "select a from t order by a limit 3", + rowMode: 'stmt', // stmt === the default + callback: function(row){ + log("row ",++this.counter,"get(0) =",row.get(0)); + }.bind({counter: 0}) + }); + + log("Query data with exec() using rowMode INTEGER (result column index)..."); + db.exec({ + sql: "select a, b from t order by a limit 3", + rowMode: 1, // === result column 1 + callback: function(row){ + log("row ",++this.counter,"b =",row); + }.bind({counter: 0}) + }); + + log("Query data with exec() without a callback..."); + let resultRows = []; + db.exec({ + sql: "select a, b from t order by a limit 3", + rowMode: 'object', + resultRows: resultRows + }); + log("Result rows:",resultRows); + + log("Create a scalar UDF..."); + db.createFunction({ + name: 'twice', + callback: function(arg){ // note the call arg count + return arg + arg; + } + }); + log("Run scalar UDF and collect result column names..."); + let columnNames = []; + db.exec({ + sql: "select a, twice(a), twice(''||a) from t order by a desc limit 3", + columnNames: columnNames, + callback: function(row){ + log("a =",row.get(0), "twice(a) =", row.get(1), "twice(''||a) =",row.get(2)); + } + }); + log("Result column names:",columnNames); + + /** + Main differences between exec() and execMulti(): + + - execMulti() traverses all statements in the input SQL + + - exec() supports a couple options not supported by execMulti(), + and vice versa. + + - execMulti() result callback/array only activates for the + first statement which has result columns. It is arguable + whether it should support a callback at all, and that + feature may be removed. + + - execMulti() column-bind data only activates for the first statement + with bindable columns. This feature is arguable and may be removed. + */ + + if(0){ + warn("UDF will throw because of incorrect arg count..."); + db.exec("select twice(1,2,3)"); + } + + try { + db.callInTransaction( function(D) { + D.exec("delete from t"); + log("In transaction: count(*) from t =",db.selectValue("select count(*) from t")); + throw new sqlite3.SQLite3Error("Demonstrating callInTransaction() rollback"); + }); + }catch(e){ + log("Got expected exception:",e.message); + log("count(*) from t =",db.selectValue("select count(*) from t")); + } + + }finally{ + db.close(); + } + + /** + Misc. DB features: + + - get change count (total or statement-local, 32- or 64-bit) + - get its file name + - selectValue() takes SQL and returns first column of first row. + + Misc. Stmt features: + + - Various forms of bind() + - clearBindings() + - reset() + - Various forms of step() + - Variants of get() for explicit type treatment/conversion, + e.g. getInt(), getFloat(), getBlob(), getJSON() + - getColumnName(ndx), getColumnNames() + - getParamIndex(name) + */ + }/*demo1()*/; + + const runDemos = function(Module){ + //log("Module",Module); + const sqlite3 = Module.sqlite3, + capi = sqlite3.capi, + oo = sqlite3.oo1, + wasm = capi.wasm; + log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); + try { + [ demo1 ].forEach((f)=>f(sqlite3, Module)) + }catch(e){ + error("Exception:",e.message); + throw e; + } + }; + + sqlite3InitModule(self.sqlite3TestModule).then(runDemos); +})(); diff --git a/ext/wasm/testing1.html b/ext/wasm/testing1.html index 0c64470221..24c1e82360 100644 --- a/ext/wasm/testing1.html +++ b/ext/wasm/testing1.html @@ -27,7 +27,7 @@
Most stuff on this page happens in the dev console.

- + diff --git a/manifest b/manifest index c19c285c0d..df72b2cc8a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C wasm:\smove\sanother\sfile\sand\supdate\stesting1/testing2\sto\saccount\sfor\s[e38d00c2b82d].\sDisable\swasmfs\sby\sdefault\sas\sit\sbreaks\sthe\sworker-based\smodule\sloader\s(reason\sas\syet\sunknown). -D 2022-08-16T16:16:25.326 +C wasm:\sadd\sa\ssmall\sdemo/presentation\sapp\sfor\sJS\sOO\sAPI\s#1\sand\smake\sa\sfew\sminor\sadditions\sto\sthat\sAPI. +D 2022-08-16T16:36:19.533 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -483,7 +483,7 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 149fd63a0400cd1d69548887ffde2ed89c13283384a63c2e9fcfc695e38a9e11 F ext/wasm/api/sqlite3-api-glue.js 82c09f49c69984009ba5af2b628e67cc26c5dd203d383cd3091d40dab4e6514b -F ext/wasm/api/sqlite3-api-oo1.js a3469bbb217b9787ba9aa6216423ec55cf9457fecefb9698e433d0e1cc4cc918 +F ext/wasm/api/sqlite3-api-oo1.js 1d63e7e453e38ff2ad0c5e8bf68345f6fc5fe99fbc4a893cc982b4c50d904ca0 F ext/wasm/api/sqlite3-api-opfs.js c93cdd14f81a26b3a64990515ee05c7e29827fbc8fba4e4c2fef3a37a984db89 F ext/wasm/api/sqlite3-api-prologue.js c0f335bf8b44071da0204b8fa95ce78fd737033b155e7bcfdaee6ae64600802f F ext/wasm/api/sqlite3-api-worker.js 1124f404ecdf3c14d9f829425cef778cd683911a9883f0809a463c3c7773c9fd @@ -493,6 +493,8 @@ F ext/wasm/common/SqliteTestUtil.js e41a1406f18da9224523fad0c48885caf995b56956a5 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 F ext/wasm/common/whwasmutil.js 41b8e097e0a9cb07c24c0ede3c81b72470a63f4a4efb07f75586dc131569f5ae +F ext/wasm/demo-oo1.html 75646855b38405d82781246fd08c852a2b3bee05dd9f0fe10ab655a8cffb79aa +F ext/wasm/demo-oo1.js 0b1f85ee622b8f0ffe133ed88584bfc6b1ef1dcbe5b605278073e4694ebd0a2f F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 @@ -506,8 +508,8 @@ F ext/wasm/scratchpad-opfs-main.js a819ed26047c5539630cea59add6a5082ba04cdf82da2 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 F ext/wasm/scratchpad-opfs-worker2.js de3ea77548243a638c8426b7e43cc1dbfc511013228ab98436eb102923ed6689 -F ext/wasm/sqlite3-worker.js 1325ca8d40129a82531902a3a077b795db2eeaee81746e5a0c811a04b415fa7f w ext/wasm/api/sqlite3-worker.js -F ext/wasm/testing1.html 0bf3ff224628c1f1e3ed22a2dc1837c6c73722ad8c0ad9c8e6fb9e6047667231 +F ext/wasm/sqlite3-worker.js 1325ca8d40129a82531902a3a077b795db2eeaee81746e5a0c811a04b415fa7f +F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409 F ext/wasm/testing1.js a25069e20d5f8dc548cc98bcf7002cec812084421a1f7f70ffae2c706d1167b2 F ext/wasm/testing2.html 73e5048e666fd6fb28b6e635677a9810e1e139c599ddcf28d687c982134b92b8 F ext/wasm/testing2.js dbb825174878716fab42c340962c0c1b32bfbe26dd16c7b082d30d35f510466c @@ -2004,8 +2006,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 41762f9518bb51b8b23ae6507628d6d3256044e1f2aca6e7251dc57722062c42 -R 8ceb5e9180bc147ed0af56485cecdca6 +P 6dad5e0573ee866657ee10b43e55b86fc9caac7a66c13bdbd35c3625a4783f14 +R 21ffe0bceeaf14b3e3d3b64b375f9eb8 U stephan -Z 7a0a645037fbcb33d5a75b781a0d284d +Z cfe198e466da02226e0ffe831d5c4056 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3e6aa5a962..bbadb22b44 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6dad5e0573ee866657ee10b43e55b86fc9caac7a66c13bdbd35c3625a4783f14 \ No newline at end of file +d6d79b661a1c6137d4693393e02416da4858d58dc84d144081a48d523655b483 \ No newline at end of file From 6e901b07a3611d6b865e390f117aa4c116d47192 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 16 Aug 2022 17:29:59 +0000 Subject: [PATCH 014/428] wasm: minor cleanups in the OO API #1 demo. FossilOrigin-Name: b9cdcc06a8f70694d10ee5b1a0fd9f08f4c705ce576e5103bbafb36fc9cc2122 --- ext/wasm/demo-oo1.js | 4 ++-- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/wasm/demo-oo1.js b/ext/wasm/demo-oo1.js index 2792b50920..bb5e9fd267 100644 --- a/ext/wasm/demo-oo1.js +++ b/ext/wasm/demo-oo1.js @@ -15,14 +15,13 @@ */ 'use strict'; (function(){ - const T = self.SqliteTestUtil; const toss = function(...args){throw new Error(args.join(' '))}; const debug = console.debug.bind(console), log = console.log.bind(console), warn = console.warn.bind(console), error = console.error.bind(console); - const demo1 = function(sqlite3,EmModule){ + const demo1 = function(sqlite3){ const capi = sqlite3.capi, oo = sqlite3.oo1, wasm = capi.wasm; @@ -206,6 +205,7 @@ oo = sqlite3.oo1, wasm = capi.wasm; log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); + log("sqlite3 namespace:",sqlite3); try { [ demo1 ].forEach((f)=>f(sqlite3, Module)) }catch(e){ diff --git a/manifest b/manifest index df72b2cc8a..ccd0bb633d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C wasm:\sadd\sa\ssmall\sdemo/presentation\sapp\sfor\sJS\sOO\sAPI\s#1\sand\smake\sa\sfew\sminor\sadditions\sto\sthat\sAPI. -D 2022-08-16T16:36:19.533 +C wasm:\sminor\scleanups\sin\sthe\sOO\sAPI\s#1\sdemo. +D 2022-08-16T17:29:59.160 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -494,7 +494,7 @@ F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 F ext/wasm/common/whwasmutil.js 41b8e097e0a9cb07c24c0ede3c81b72470a63f4a4efb07f75586dc131569f5ae F ext/wasm/demo-oo1.html 75646855b38405d82781246fd08c852a2b3bee05dd9f0fe10ab655a8cffb79aa -F ext/wasm/demo-oo1.js 0b1f85ee622b8f0ffe133ed88584bfc6b1ef1dcbe5b605278073e4694ebd0a2f +F ext/wasm/demo-oo1.js a036f93a41815063cca77e896a19bdd3b9771cd9dc93aa34b308245e1c9405a5 F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 @@ -2006,8 +2006,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 6dad5e0573ee866657ee10b43e55b86fc9caac7a66c13bdbd35c3625a4783f14 -R 21ffe0bceeaf14b3e3d3b64b375f9eb8 +P d6d79b661a1c6137d4693393e02416da4858d58dc84d144081a48d523655b483 +R 495d381dcb72f9436340c5a0c3371031 U stephan -Z cfe198e466da02226e0ffe831d5c4056 +Z f43bcfbf56838f97d3048cbeb389d31b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bbadb22b44..8536e5e333 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d6d79b661a1c6137d4693393e02416da4858d58dc84d144081a48d523655b483 \ No newline at end of file +b9cdcc06a8f70694d10ee5b1a0fd9f08f4c705ce576e5103bbafb36fc9cc2122 \ No newline at end of file From 453af2f6ccd617b0bed30a958e94919097393876 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 17 Aug 2022 16:44:05 +0000 Subject: [PATCH 015/428] Minor cleanups, reorgs, and doc updates for the JS APIs. Renamed sqlite3(-api)-worker.js to sqlite3(-api)-worker1.js, for symmetry with sqlite3-api-oo1.js. FossilOrigin-Name: f5059ee6f9fc55a381cbf08a30dfb9a5636c0b44341e42f4e9f12a3b109b5507 --- ext/wasm/GNUmakefile | 2 +- ext/wasm/api/README.md | 8 +- ext/wasm/api/sqlite3-api-glue.js | 13 +- ext/wasm/api/sqlite3-api-oo1.js | 19 +- ext/wasm/api/sqlite3-api-prologue.js | 102 ++++---- ...3-api-worker.js => sqlite3-api-worker1.js} | 217 +++++++++--------- .../{sqlite3-worker.js => sqlite3-worker1.js} | 4 +- ext/wasm/testing1.js | 4 +- ext/wasm/testing2.html | 4 +- ext/wasm/testing2.js | 20 +- manifest | 30 +-- manifest.uuid | 2 +- 12 files changed, 214 insertions(+), 211 deletions(-) rename ext/wasm/api/{sqlite3-api-worker.js => sqlite3-api-worker1.js} (80%) rename ext/wasm/{sqlite3-worker.js => sqlite3-worker1.js} (94%) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 8a9adb75f5..467034f9e8 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -128,7 +128,7 @@ sqlite3-api.jses := \ $(dir.jacc)/jaccwabyt.js \ $(dir.api)/sqlite3-api-glue.js \ $(dir.api)/sqlite3-api-oo1.js \ - $(dir.api)/sqlite3-api-worker.js + $(dir.api)/sqlite3-api-worker1.js #sqlite3-api.jses += $(dir.api)/sqlite3-api-opfs.js sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js diff --git a/ext/wasm/api/README.md b/ext/wasm/api/README.md index 43d2b0dd5d..9000697b26 100644 --- a/ext/wasm/api/README.md +++ b/ext/wasm/api/README.md @@ -60,17 +60,17 @@ browser client: high-level sqlite3 JS wrappers and should feel relatively familiar to anyone familiar with such APIs. That said, it is not a "required component" and can be elided from builds which do not want it. -- `sqlite3-api-worker.js`\ +- `sqlite3-api-worker1.js`\ A Worker-thread-based API which uses OO API #1 to provide an interface to a database which can be driven from the main Window thread via the Worker message-passing interface. Like OO API #1, this is an optional component, offering one of any number of potential implementations for such an API. - - `sqlite3-worker.js`\ + - `sqlite3-worker1.js`\ Is not part of the amalgamated sources and is intended to be loaded by a client Worker thread. It loads the sqlite3 module - and runs the Worker API which is implemented in - `sqlite3-api-worker.js`. + and runs the Worker #1 API which is implemented in + `sqlite3-api-worker1.js`. - `sqlite3-api-opfs.js`\ is an in-development/experimental sqlite3 VFS wrapper, the goal of which being to use Google Chrome's Origin-Private FileSystem (OPFS) diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index e962c93b64..efcd6fea9b 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -128,7 +128,7 @@ */ __prepare.basic = wasm.xWrap('sqlite3_prepare_v3', "int", ["sqlite3*", "string", - "int"/*MUST always be negative*/, + "int"/*ignored for this impl!*/, "int", "**", "**"/*MUST be 0 or null or undefined!*/]); /** @@ -148,19 +148,10 @@ /* Documented in the api object's initializer. */ capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){ - /* 2022-07-08: xWrap() 'string' arg handling may be able do this - special-case handling for us. It needs to be tested. Or maybe - not: we always want to treat pzTail as null when passed a - non-pointer SQL string and the argument adapters don't have - enough state to know that. Maybe they could/should, by passing - the currently-collected args as an array as the 2nd arg to the - argument adapters? Or maybe we collect all args in an array, - pass that to an optional post-args-collected callback, and give - it a chance to manipulate the args before we pass them on? */ if(util.isSQLableTypedArray(sql)) sql = util.typedArrayToString(sql); switch(typeof sql){ case 'string': return __prepare.basic(pDb, sql, -1, prepFlags, ppStmt, null); - case 'number': return __prepare.full(pDb, sql, sqlLen||-1, prepFlags, ppStmt, pzTail); + case 'number': return __prepare.full(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail); default: return util.sqlite3_wasm_db_error( pDb, capi.SQLITE_MISUSE, diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index e54e27ec2b..9efc38b915 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -322,15 +322,14 @@ } }, /** - 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`. + 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 capi.sqlite3_db_filename(affirmDbOpen(this).pointer, dbName||"main"); + fileName: function(dbName='main'){ + return capi.sqlite3_db_filename(affirmDbOpen(this).pointer, dbName); }, /** Returns true if this db instance has a name which resolves to a @@ -759,7 +758,7 @@ capi.sqlite3_result_null(pCx); break; }else if(util.isBindableTypedArray(val)){ - const pBlob = capi.wasm.mallocFromTypedArray(val); + const pBlob = capi.wasm.allocFromTypedArray(val); capi.sqlite3_result_blob(pCx, pBlob, val.byteLength, capi.SQLITE_TRANSIENT); capi.wasm.dealloc(pBlob); @@ -1072,7 +1071,7 @@ capi.wasm.scopedAllocPop(stack); } }else{ - const pBlob = capi.wasm.mallocFromTypedArray(val); + const pBlob = capi.wasm.allocFromTypedArray(val); try{ rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength, capi.SQLITE_TRANSIENT); diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 08e3ebf6ed..cf9fd896a6 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -172,36 +172,6 @@ self.sqlite3ApiBootstrap = function(config){ counterparts. */ const capi = { - /** - An Error subclass which is thrown by this object's alloc() method - on OOM. - */ - WasmAllocError: WasmAllocError, - /** - The API's one single point of access to the WASM-side memory - allocator. Works like malloc(3) (and is likely bound to - malloc()) but throws an WasmAllocError if allocation fails. It is - important that any code which might pass through the sqlite3 C - API NOT throw and must instead return SQLITE_NOMEM (or - equivalent, depending on the context). - - That said, very few cases in the API can result in - client-defined functions propagating exceptions via the C-style - API. Most notably, this applies ot User-defined SQL Functions - (UDFs) registered via sqlite3_create_function_v2(). For that - specific case it is recommended that all UDF creation be - funneled through a utility function and that a wrapper function - be added around the UDF which catches any exception and sets - the error state to OOM. (The overall complexity of registering - UDFs essentially requires a helper for doing so!) - */ - alloc: undefined/*installed later*/, - /** - The API's one single point of access to the WASM-side memory - deallocator. Works like free(3) (and is likely bound to - free()). - */ - dealloc: undefined/*installed later*/, /** When using sqlite3_open_v2() it is important to keep the following in mind: @@ -365,6 +335,33 @@ self.sqlite3ApiBootstrap = function(config){ || toss("API config object requires a WebAssembly.Memory object", "in either config.exports.memory (exported)", "or config.memory (imported)."), + + /** + The API's one single point of access to the WASM-side memory + allocator. Works like malloc(3) (and is likely bound to + malloc()) but throws an WasmAllocError if allocation fails. It is + important that any code which might pass through the sqlite3 C + API NOT throw and must instead return SQLITE_NOMEM (or + equivalent, depending on the context). + + That said, very few cases in the API can result in + client-defined functions propagating exceptions via the C-style + API. Most notably, this applies ot User-defined SQL Functions + (UDFs) registered via sqlite3_create_function_v2(). For that + specific case it is recommended that all UDF creation be + funneled through a utility function and that a wrapper function + be added around the UDF which catches any exception and sets + the error state to OOM. (The overall complexity of registering + UDFs essentially requires a helper for doing so!) + */ + alloc: undefined/*installed later*/, + /** + The API's one single point of access to the WASM-side memory + deallocator. Works like free(3) (and is likely bound to + free()). + */ + dealloc: undefined/*installed later*/ + /* Many more wasm-related APIs get installed later on. */ }/*wasm*/ }/*capi*/; @@ -387,7 +384,7 @@ self.sqlite3ApiBootstrap = function(config){ Int8Array types and will throw if srcTypedArray is of any other type. */ - capi.wasm.mallocFromTypedArray = function(srcTypedArray){ + capi.wasm.allocFromTypedArray = function(srcTypedArray){ affirmBindableTypedArray(srcTypedArray); const pRet = this.alloc(srcTypedArray.byteLength || 1); this.heapForSize(srcTypedArray.constructor).set(srcTypedArray.byteLength ? srcTypedArray : [0], pRet); @@ -400,11 +397,13 @@ self.sqlite3ApiBootstrap = function(config){ const f = capi.wasm.exports[key]; if(!(f instanceof Function)) toss("Missing required exports[",key,"] function."); } + capi.wasm.alloc = function(n){ const m = this.exports[keyAlloc](n); if(!m) throw new WasmAllocError("Failed to allocate "+n+" bytes."); return m; }.bind(capi.wasm) + capi.wasm.dealloc = (m)=>capi.wasm.exports[keyDealloc](m); /** @@ -554,7 +553,8 @@ self.sqlite3ApiBootstrap = function(config){ ["sqlite3_value_text", "string", "*"], ["sqlite3_value_type", "int", "*"], ["sqlite3_vfs_find", "*", "string"], - ["sqlite3_vfs_register", "int", "*", "int"] + ["sqlite3_vfs_register", "int", "*", "int"], + ["sqlite3_wasm_vfs_unlink", "int", "string"] ]/*capi.wasm.bindingSignatures*/; if(false && capi.wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){ @@ -583,18 +583,26 @@ self.sqlite3ApiBootstrap = function(config){ If the wasm environment has a persistent storage directory, its path is returned by this function. If it does not then - it returns one of: + it returns "" (noting that "" is a falsy value). - - `undefined` if initIfNeeded is false and this function has - never been called before. + The first time this is called, this function inspects the current + environment to determine whether persistence filesystem support + is available and, if it is, enables it (if needed). - - `""` if no persistent storage is available. + TODOs and caveats: - Note that in both cases the return value is falsy. + - The directory name (mount point) for persistent storage is + currently hard-coded. It needs to be configurable. + + - If persistent storage is available at the root of the virtual + filesystem, this interface cannot currently distinguish that + from the lack of persistence. That case cannot currently (with + WASMFS/OPFS) happen, but it is conceivably possible in future + environments or non-browser runtimes (none of which are yet + supported targets). */ - capi.sqlite3_web_persistent_dir = function(initIfNeeded=true){ + capi.sqlite3_web_persistent_dir = function(){ if(undefined !== __persistentDir) return __persistentDir; - else if(!initIfNeeded) return; // If we have no OPFS, there is no persistent dir if(!self.FileSystemHandle || !self.FileSystemDirectoryHandle || !self.FileSystemFileHandle){ @@ -625,8 +633,22 @@ self.sqlite3ApiBootstrap = function(config){ } }.bind(capi); + /** + Returns true if sqlite3.capi.sqlite3_web_persistent_dir() is a + non-empty string and the given name has that string as its + prefix, else returns false. + */ + capi.sqlite3_web_filename_is_persistent = function(name){ + const p = this.sqlite3_web_persistent_dir(); + return (p && name) ? name.startsWith(p) : false; + }.bind(capi); + /* The remainder of the API will be set up in later steps. */ return { + /** + An Error subclass which is thrown by this.wasm.alloc() on OOM. + */ + WasmAllocError: WasmAllocError, capi, postInit: [ /* some pieces of the API may install functions into this array, @@ -635,8 +657,6 @@ self.sqlite3ApiBootstrap = function(config){ the current global object and sqlite3 is the object returned from sqlite3ApiBootstrap(). This array will be removed at the end of the API setup process. */], - /** Config is needed downstream for gluing pieces together. It - will be removed at the end of the API setup process. */ config }; }/*sqlite3ApiBootstrap()*/; diff --git a/ext/wasm/api/sqlite3-api-worker.js b/ext/wasm/api/sqlite3-api-worker1.js similarity index 80% rename from ext/wasm/api/sqlite3-api-worker.js rename to ext/wasm/api/sqlite3-api-worker1.js index 95b27b21ec..565946bbc6 100644 --- a/ext/wasm/api/sqlite3-api-worker.js +++ b/ext/wasm/api/sqlite3-api-worker1.js @@ -10,26 +10,40 @@ *********************************************************************** - This file implements a Worker-based wrapper around SQLite3 OO API - #1. + This file implements the initializer for the sqlite3 "Worker API + #1", a very basic DB access API intended to be scripted from a main + window thread via Worker-style messages. Because of limitations in + that type of communication, this API is minimalistic and only + capable of serving relatively basic DB requests (e.g. it cannot + process nested query loops concurrently). + + This file requires that the core C-style sqlite3 API and OO API #1 + have been loaded. +*/ + +/** + This function implements a Worker-based wrapper around SQLite3 OO + API #1, colloquially known as "Worker API #1". In order to permit this API to be loaded in worker threads without automatically registering onmessage handlers, initializing the - worker API requires calling initWorkerAPI(). If this function + worker API requires calling initWorker1API(). If this function is called from a non-worker thread then it throws an exception. - When initialized, it installs message listeners to receive messages - from the main thread and then it posts a message in the form: + When initialized, it installs message listeners to receive Worker + messages and then it posts a message in the form: ``` - {type:'sqlite3-api',data:'worker-ready'} + {type:'sqlite3-api',data:'worker1-ready'} ``` - This file requires that the core C-style sqlite3 API and OO API #1 - have been loaded and that self.sqlite3 contains both, - as documented for those APIs. + to let the client know that it has been initialized. Clients may + optionally depend on this function not returning until + initialization is complete, as the initialization is synchronous. + In some contexts, however, listening for the above message is + a better fit. */ -self.sqlite3.initWorkerAPI = function(){ +self.sqlite3.initWorker1API = function(){ 'use strict'; /** UNDER CONSTRUCTION @@ -39,39 +53,12 @@ self.sqlite3.initWorkerAPI = function(){ cannot pass callback functions between the window thread and a worker thread, so we have to receive all db results via asynchronous message-passing. That requires an asychronous API - with a distinctly different shape that the main OO API. + with a distinctly different shape than OO API #1. - Certain important considerations here include: + TODOs include, but are not necessarily limited to: - - Support only one db connection or multiple? The former is far - easier, but there's always going to be a user out there who wants - to juggle six database handles at once. Do we add that complexity - or tell such users to write their own code using the provided - lower-level APIs? - - - Fetching multiple results: do we pass them on as a series of - messages, with start/end messages on either end, or do we collect - all results and bundle them back in a single message? The former - is, generically speaking, more memory-efficient but the latter - far easier to implement in this environment. The latter is - untennable for large data sets. Despite a web page hypothetically - being a relatively limited environment, there will always be - those users who feel that they should/need to be able to work - with multi-hundred-meg (or larger) blobs, and passing around - arrays of those may quickly exhaust the JS engine's memory. - - TODOs include, but are not limited to: - - - The ability to manage multiple DB handles. This can - potentially be done via a simple mapping of DB.filename or - DB.pointer (`sqlite3*` handle) to DB objects. The open() - interface would need to provide an ID (probably DB.pointer) back - to the user which can optionally be passed as an argument to - the other APIs (they'd default to the first-opened DB, for - ease of use). Client-side usability of this feature would - benefit from making another wrapper class (or a singleton) - available to the main thread, with that object proxying all(?) - communication with the worker. + - Support for handling multiple DBs via this interface is under + development. - Revisit how virtual files are managed. We currently delete DBs from the virtual filesystem when we close them, for the sake of @@ -79,6 +66,8 @@ self.sqlite3.initWorkerAPI = function(){ require that we give up that habit. Similarly, fully supporting ATTACH, where a user can upload multiple DBs and ATTACH them, also requires the that we manage the VFS entries better. + Related: we most definitely do not want to delete persistent DBs + (e.g. stored on OPFS) when they're closed. */ const toss = (...args)=>{throw new Error(args.join(' '))}; if('function' !== typeof importScripts){ @@ -116,8 +105,7 @@ self.sqlite3.initWorkerAPI = function(){ // same filename and close/reopen it (or just pass it back as is?). if(!arg && this.defaultDb) return this.defaultDb; //???if(this.defaultDb) this.defaultDb.close(); - let db; - db = (Array.isArray(arg) ? new DB(...arg) : new DB(arg)); + const db = (Array.isArray(arg) ? new DB(...arg) : new DB(arg)); this.dbs[getDbId(db)] = db; if(!this.defaultDb) this.defaultDb = db; return db; @@ -125,13 +113,17 @@ self.sqlite3.initWorkerAPI = function(){ close: function(db,alsoUnlink){ if(db){ delete this.dbs[getDbId(db)]; - db.close(alsoUnlink); + const filename = db.fileName(); + db.close(); if(db===this.defaultDb) this.defaultDb = undefined; + if(alsoUnlink && filename){ + sqlite3.capi.sqlite3_wasm_vfs_unlink(filename); + } } }, post: function(type,data,xferList){ if(xferList){ - self.postMessage({type, data},xferList); + self.postMessage( {type, data}, xferList ); xferList.length = 0; }else{ self.postMessage({type, data}); @@ -172,6 +164,68 @@ self.sqlite3.initWorkerAPI = function(){ */ const wMsgHandler = { xfer: [/*Temp holder for "transferable" postMessage() state.*/], + /** + Proxy for the DB constructor. Expects to be passed a single + object or a falsy value to use defaults. The object may + have a filename property to name the db file (see the DB + constructor for peculiarities and transformations) and/or a + buffer property (a Uint8Array holding a complete database + file's contents). The response is an object: + + { + filename: db filename (possibly differing from the input), + + dbId: an opaque ID value which must be passed to other calls + in this API to tell them which db to use. If it is not + provided to future calls, they will default to + operating on the first-opened db. + + messageId: if the client-sent message included this field, + it is mirrored in the response. + } + */ + open: function(ev){ + const args = [], data = (ev.data || {}); + if(data.simulateError){ // undocumented internal testing option + toss("Throwing because of simulateError flag."); + } + if(data.filename) args.push(data.filename); + const db = wState.open(args); + return { + filename: db.filename, + dbId: getDbId(db) + }; + }, + /** + Proxy for DB.close(). If ev.data may either be a boolean or + an object with an `unlink` property. If that value is + truthy then the db file (if the db is currently open) will + be unlinked from the virtual filesystem, else it will be + kept intact. The response object is: + + { + filename: db filename _if_ the db is opened when this + is called, else the undefined value, + unlink: boolean. If true, unlink() (delete) the db file + after closing int. Any error while deleting it is + ignored. + } + + It does not error if the given db is already closed or no db is + provided. It is simply does nothing useful in that case. + */ + close: function(ev){ + const db = getMsgDb(ev,false); + const response = { + filename: db && db.filename, + dbId: db ? getDbId(db) : undefined + }; + if(db){ + wState.close(db, !!((ev.data && 'object'===typeof ev.data) + ? ev.data.unlink : false)); + } + return response; + }, /** Proxy for DB.exec() which expects a single argument of type string (SQL to execute) or an options object in the form @@ -266,10 +320,16 @@ self.sqlite3.initWorkerAPI = function(){ exports of ":memory:" and "" (temp file) DBs. The latter is ostensibly easy because the file is (potentially) on disk, but the former does not have a structure which maps directly to a - db file image. + db file image. We can VACUUM INTO a :memory:/temp db into a + file for that purpose, though. */ export: function(ev){ toss("export() requires reimplementing for portability reasons."); + /** + We need to reimplement this to use the Emscripten FS + interface. That part used to be in the OO#1 API but that + dependency was removed from that level of the API. + */ /**const db = getMsgDb(ev); const response = { buffer: db.exportBinaryImage(), @@ -279,66 +339,6 @@ self.sqlite3.initWorkerAPI = function(){ this.xfer.push(response.buffer.buffer); return response;**/ }/*export()*/, - /** - Proxy for the DB constructor. Expects to be passed a single - object or a falsy value to use defaults. The object may - have a filename property to name the db file (see the DB - constructor for peculiarities and transformations) and/or a - buffer property (a Uint8Array holding a complete database - file's contents). The response is an object: - - { - filename: db filename (possibly differing from the input), - - id: an opaque ID value intended for future distinction - between multiple db handles. Messages including a specific - ID will use the DB for that ID. - - } - - If the Worker's db is currently opened, this call closes it - before proceeding. - */ - open: function(ev){ - wState.close(/*true???*/); - const args = [], data = (ev.data || {}); - if(data.simulateError){ - toss("Throwing because of open.simulateError flag."); - } - if(data.filename) args.push(data.filename); - if(data.buffer){ - args.push(data.buffer); - this.xfer.push(data.buffer.buffer); - } - const db = wState.open(args); - return { - filename: db.filename, - dbId: getDbId(db) - }; - }, - /** - Proxy for DB.close(). If ev.data may either be a boolean or - an object with an `unlink` property. If that value is - truthy then the db file (if the db is currently open) will - be unlinked from the virtual filesystem, else it will be - kept intact. The response object is: - - { - filename: db filename _if_ the db is opened when this - is called, else the undefined value - } - */ - close: function(ev){ - const db = getMsgDb(ev,false); - const response = { - filename: db && db.filename - }; - if(db){ - wState.close(db, !!((ev.data && 'object'===typeof ev.data) - ? ev.data.unlink : ev.data)); - } - return response; - }, toss: function(ev){ toss("Testing worker exception"); } @@ -352,7 +352,8 @@ self.sqlite3.initWorkerAPI = function(){ { type: apiCommand, dbId: optional DB ID value (else uses a default db handle) - data: apiArguments + data: apiArguments, + messageId: optional client-specific value } As a rule, these commands respond with a postMessage() of their @@ -416,5 +417,5 @@ self.sqlite3.initWorkerAPI = function(){ response.departureTime = ev.departureTime; wState.post(evType, response, wMsgHandler.xfer); }; - setTimeout(()=>self.postMessage({type:'sqlite3-api',data:'worker-ready'}), 0); + setTimeout(()=>self.postMessage({type:'sqlite3-api',data:'worker1-ready'}), 0); }.bind({self, sqlite3: self.sqlite3}); diff --git a/ext/wasm/sqlite3-worker.js b/ext/wasm/sqlite3-worker1.js similarity index 94% rename from ext/wasm/sqlite3-worker.js rename to ext/wasm/sqlite3-worker1.js index 48797de8ab..3982ddaa2b 100644 --- a/ext/wasm/sqlite3-worker.js +++ b/ext/wasm/sqlite3-worker1.js @@ -14,7 +14,7 @@ sqlite3.js, initializes the module, and postMessage()'s a message after the module is initialized: - {type: 'sqlite3-api', data: 'worker-ready'} + {type: 'sqlite3-api', data: 'worker1-ready'} This seemingly superfluous level of indirection is necessary when loading sqlite3.js via a Worker. Instantiating a worker with new @@ -28,4 +28,4 @@ */ "use strict"; importScripts('sqlite3.js'); -sqlite3InitModule().then((EmscriptenModule)=>EmscriptenModule.sqlite3.initWorkerAPI()); +sqlite3InitModule().then((EmscriptenModule)=>EmscriptenModule.sqlite3.initWorker1API()); diff --git a/ext/wasm/testing1.js b/ext/wasm/testing1.js index 779e0bd72f..28f2b604b4 100644 --- a/ext/wasm/testing1.js +++ b/ext/wasm/testing1.js @@ -163,10 +163,10 @@ } try { - throw new capi.WasmAllocError; + throw new sqlite3.WasmAllocError; }catch(e){ T.assert(e instanceof Error) - .assert(e instanceof capi.WasmAllocError); + .assert(e instanceof sqlite3.WasmAllocError); } try { diff --git a/ext/wasm/testing2.html b/ext/wasm/testing2.html index 739c7f66be..25d5a9f5d8 100644 --- a/ext/wasm/testing2.html +++ b/ext/wasm/testing2.html @@ -6,10 +6,10 @@ - sqlite3-worker.js tests + sqlite3-worker1.js tests -
sqlite3-worker.js tests
+
sqlite3-worker1.js tests
diff --git a/ext/wasm/testing2.js b/ext/wasm/testing2.js index bc578bb792..bcbe7b50de 100644 --- a/ext/wasm/testing2.js +++ b/ext/wasm/testing2.js @@ -10,12 +10,12 @@ *********************************************************************** - A basic test script for sqlite3-worker.js. + A basic test script for sqlite3-worker1.js. */ 'use strict'; (function(){ const T = self.SqliteTestUtil; - const SW = new Worker("sqlite3-worker.js"); + const SW = new Worker("sqlite3-worker1.js"); const DbState = { id: undefined }; @@ -48,7 +48,7 @@ }; let startTime; - + /** A queue for callbacks which are to be run in response to async DB commands. See the notes in runTests() for why we need @@ -261,16 +261,8 @@ will fail and we have no way of cancelling them once they've been posted to the worker. - We currently do (2) because (A) it's certainly the most - client-friendly thing to do and (B) it seems likely that most - apps using this API will only have a single db to work with so - won't need to juggle multiple DB ids. If we revert to (1) then - the following call to runTests2() needs to be moved into the - callback function of the runOneTest() check for the 'open' - command. Note, also, that using approach (2) does not keep the - user from instead using approach (1), noting that doing so - requires explicit handling of the 'open' message to account for - it. + Which approach we use below depends on the boolean value of + waitForOpen. */ const waitForOpen = 1, simulateOpenError = 0 /* if true, the remaining tests will @@ -315,7 +307,7 @@ switch(ev.type){ case 'sqlite3-api': switch(ev.data){ - case 'worker-ready': + case 'worker1-ready': log("Message:",ev); self.sqlite3TestModule.setStatus(null); runTests(); diff --git a/manifest b/manifest index ccd0bb633d..b1aff51e1e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C wasm:\sminor\scleanups\sin\sthe\sOO\sAPI\s#1\sdemo. -D 2022-08-16T17:29:59.160 +C Minor\scleanups,\sreorgs,\sand\sdoc\supdates\sfor\sthe\sJS\sAPIs.\sRenamed\ssqlite3(-api)-worker.js\sto\ssqlite3(-api)-worker1.js,\sfor\ssymmetry\swith\ssqlite3-api-oo1.js. +D 2022-08-17T16:44:05.090 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,19 +474,19 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile b5b34ee01efcf7c3d6c53d54a332545f2670e7a5a0c63d6de847d3726e5bc521 +F ext/wasm/GNUmakefile 19c21ce4df5583278cc495bffbe03c69dc64af60fa9dac01766d0192bd191ac6 F ext/wasm/README.md 4b00ae7c7d93c4591251245f0996a319e2651361013c98d2efb0b026771b7331 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 -F ext/wasm/api/README.md b6d0fb64bfdf7bf9ce6938ea4104228f6f5bbef600f5d910b2f8c8694195988c +F ext/wasm/api/README.md d876597edd2b9542b6ea031adaaff1c042076fde7b670b1dc6d8a87b28a6631b F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 149fd63a0400cd1d69548887ffde2ed89c13283384a63c2e9fcfc695e38a9e11 -F ext/wasm/api/sqlite3-api-glue.js 82c09f49c69984009ba5af2b628e67cc26c5dd203d383cd3091d40dab4e6514b -F ext/wasm/api/sqlite3-api-oo1.js 1d63e7e453e38ff2ad0c5e8bf68345f6fc5fe99fbc4a893cc982b4c50d904ca0 +F ext/wasm/api/sqlite3-api-glue.js 4a09dd1153874f7a716cf953329bc1e78bf24e0870a9aad15214ffd51ac913bc +F ext/wasm/api/sqlite3-api-oo1.js d68dc7a692bc54385d9b851e914b386a146dc6c86624bfeb4672280a2d822082 F ext/wasm/api/sqlite3-api-opfs.js c93cdd14f81a26b3a64990515ee05c7e29827fbc8fba4e4c2fef3a37a984db89 -F ext/wasm/api/sqlite3-api-prologue.js c0f335bf8b44071da0204b8fa95ce78fd737033b155e7bcfdaee6ae64600802f -F ext/wasm/api/sqlite3-api-worker.js 1124f404ecdf3c14d9f829425cef778cd683911a9883f0809a463c3c7773c9fd +F ext/wasm/api/sqlite3-api-prologue.js 96997e411b41ff3d4024c2ad625c5cdb7b6a619ba8beece4662ad190191b1119 +F ext/wasm/api/sqlite3-api-worker1.js 74130ec4979baeaf3e909c7619b99e9f466e2d816cd07370987ff639d168ef45 w ext/wasm/api/sqlite3-api-worker.js F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0e78035045e3328fb050ec9580c6bfb714c756a1d3b917259e58baf9b0332c98 F ext/wasm/common/SqliteTestUtil.js e41a1406f18da9224523fad0c48885caf995b56956a5b9852909c0989e687e90 @@ -508,11 +508,11 @@ F ext/wasm/scratchpad-opfs-main.js a819ed26047c5539630cea59add6a5082ba04cdf82da2 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 F ext/wasm/scratchpad-opfs-worker2.js de3ea77548243a638c8426b7e43cc1dbfc511013228ab98436eb102923ed6689 -F ext/wasm/sqlite3-worker.js 1325ca8d40129a82531902a3a077b795db2eeaee81746e5a0c811a04b415fa7f +F ext/wasm/sqlite3-worker1.js e93fe8e5da7cb56dcf4d1bb0aa44bf681b509e1c56f2a75885c0f408f034c42b w ext/wasm/sqlite3-worker.js F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409 -F ext/wasm/testing1.js a25069e20d5f8dc548cc98bcf7002cec812084421a1f7f70ffae2c706d1167b2 -F ext/wasm/testing2.html 73e5048e666fd6fb28b6e635677a9810e1e139c599ddcf28d687c982134b92b8 -F ext/wasm/testing2.js dbb825174878716fab42c340962c0c1b32bfbe26dd16c7b082d30d35f510466c +F ext/wasm/testing1.js 9a97a7e45ce122b479b4a706ae1024abded67fd5f34a5764e41ff5efde8dfa17 +F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 +F ext/wasm/testing2.js e16ae385cd24c4a4ec5de6f1c02e621d243e1f179204ac8df31068faa9e31b1a F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2006,8 +2006,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d6d79b661a1c6137d4693393e02416da4858d58dc84d144081a48d523655b483 -R 495d381dcb72f9436340c5a0c3371031 +P b9cdcc06a8f70694d10ee5b1a0fd9f08f4c705ce576e5103bbafb36fc9cc2122 +R 0f6a8177d32b201ea7bdd0eb1ddeff9b U stephan -Z f43bcfbf56838f97d3048cbeb389d31b +Z 89af6a2fddda724fc088d602c326bc26 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8536e5e333..a341ff9cfa 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b9cdcc06a8f70694d10ee5b1a0fd9f08f4c705ce576e5103bbafb36fc9cc2122 \ No newline at end of file +f5059ee6f9fc55a381cbf08a30dfb9a5636c0b44341e42f4e9f12a3b109b5507 \ No newline at end of file From fed91737f5ddeaee86b2f282fea22beebad161f7 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 18 Aug 2022 11:16:27 +0000 Subject: [PATCH 016/428] Improve an exception check in demo-oo1.js. FossilOrigin-Name: 55e1b775fa7ec492817cc847036df7794c3c22b78558e9e9c513f24c775dedab --- ext/wasm/demo-oo1.js | 8 ++++++-- manifest | 16 ++++++++-------- manifest.uuid | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/ext/wasm/demo-oo1.js b/ext/wasm/demo-oo1.js index bb5e9fd267..480ea4938f 100644 --- a/ext/wasm/demo-oo1.js +++ b/ext/wasm/demo-oo1.js @@ -170,8 +170,12 @@ throw new sqlite3.SQLite3Error("Demonstrating callInTransaction() rollback"); }); }catch(e){ - log("Got expected exception:",e.message); - log("count(*) from t =",db.selectValue("select count(*) from t")); + if(e instanceof sqlite3.SQLite3Error){ + log("Got expected exception:",e.message); + log("count(*) from t =",db.selectValue("select count(*) from t")); + }else{ + throw e; + } } }finally{ diff --git a/manifest b/manifest index b1aff51e1e..9601cb65da 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\scleanups,\sreorgs,\sand\sdoc\supdates\sfor\sthe\sJS\sAPIs.\sRenamed\ssqlite3(-api)-worker.js\sto\ssqlite3(-api)-worker1.js,\sfor\ssymmetry\swith\ssqlite3-api-oo1.js. -D 2022-08-17T16:44:05.090 +C Improve\san\sexception\scheck\sin\sdemo-oo1.js. +D 2022-08-18T11:16:27.283 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -486,7 +486,7 @@ F ext/wasm/api/sqlite3-api-glue.js 4a09dd1153874f7a716cf953329bc1e78bf24e0870a9a F ext/wasm/api/sqlite3-api-oo1.js d68dc7a692bc54385d9b851e914b386a146dc6c86624bfeb4672280a2d822082 F ext/wasm/api/sqlite3-api-opfs.js c93cdd14f81a26b3a64990515ee05c7e29827fbc8fba4e4c2fef3a37a984db89 F ext/wasm/api/sqlite3-api-prologue.js 96997e411b41ff3d4024c2ad625c5cdb7b6a619ba8beece4662ad190191b1119 -F ext/wasm/api/sqlite3-api-worker1.js 74130ec4979baeaf3e909c7619b99e9f466e2d816cd07370987ff639d168ef45 w ext/wasm/api/sqlite3-api-worker.js +F ext/wasm/api/sqlite3-api-worker1.js 74130ec4979baeaf3e909c7619b99e9f466e2d816cd07370987ff639d168ef45 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0e78035045e3328fb050ec9580c6bfb714c756a1d3b917259e58baf9b0332c98 F ext/wasm/common/SqliteTestUtil.js e41a1406f18da9224523fad0c48885caf995b56956a5b9852909c0989e687e90 @@ -494,7 +494,7 @@ F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 F ext/wasm/common/whwasmutil.js 41b8e097e0a9cb07c24c0ede3c81b72470a63f4a4efb07f75586dc131569f5ae F ext/wasm/demo-oo1.html 75646855b38405d82781246fd08c852a2b3bee05dd9f0fe10ab655a8cffb79aa -F ext/wasm/demo-oo1.js a036f93a41815063cca77e896a19bdd3b9771cd9dc93aa34b308245e1c9405a5 +F ext/wasm/demo-oo1.js 09c2e3f8dab87308d945da51eef4dae206f1e0a4edc6d2207096b5617a64b651 F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 @@ -508,7 +508,7 @@ F ext/wasm/scratchpad-opfs-main.js a819ed26047c5539630cea59add6a5082ba04cdf82da2 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 F ext/wasm/scratchpad-opfs-worker2.js de3ea77548243a638c8426b7e43cc1dbfc511013228ab98436eb102923ed6689 -F ext/wasm/sqlite3-worker1.js e93fe8e5da7cb56dcf4d1bb0aa44bf681b509e1c56f2a75885c0f408f034c42b w ext/wasm/sqlite3-worker.js +F ext/wasm/sqlite3-worker1.js e93fe8e5da7cb56dcf4d1bb0aa44bf681b509e1c56f2a75885c0f408f034c42b F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409 F ext/wasm/testing1.js 9a97a7e45ce122b479b4a706ae1024abded67fd5f34a5764e41ff5efde8dfa17 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 @@ -2006,8 +2006,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P b9cdcc06a8f70694d10ee5b1a0fd9f08f4c705ce576e5103bbafb36fc9cc2122 -R 0f6a8177d32b201ea7bdd0eb1ddeff9b +P f5059ee6f9fc55a381cbf08a30dfb9a5636c0b44341e42f4e9f12a3b109b5507 +R 26de05b21625601bc9d1934cb0717538 U stephan -Z 89af6a2fddda724fc088d602c326bc26 +Z 631a9c2307319184f6094a1100613815 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a341ff9cfa..9d14975f11 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f5059ee6f9fc55a381cbf08a30dfb9a5636c0b44341e42f4e9f12a3b109b5507 \ No newline at end of file +55e1b775fa7ec492817cc847036df7794c3c22b78558e9e9c513f24c775dedab \ No newline at end of file From d869a21d132f69653eb73a50afc00b8690a669fd Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 18 Aug 2022 12:21:58 +0000 Subject: [PATCH 017/428] javascript: rename and simplify DB.callInTransaction() as DB.transaction(). Add DB.savepoint(), which works the same as transaction() but uses savepoints. Correct concatenation of arguments to SQLite3Error to use spaces instead of commas. Test that demo-oo1.js works with persistent OPFS storage (output differs due to persistent rows, but the demo works). FossilOrigin-Name: e8c323f12b6223bc9dcbbec40698969c7917128aa50cf599c247df23f607ae61 --- ext/wasm/api/sqlite3-api-oo1.js | 61 +++++++++++++++++++++++---------- ext/wasm/demo-oo1.js | 45 +++++++++++++++++------- manifest | 14 ++++---- manifest.uuid | 2 +- 4 files changed, 83 insertions(+), 39 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 9efc38b915..0e04b63ebf 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -59,12 +59,16 @@ enabling clients to unambiguously identify such exceptions. */ class SQLite3Error extends Error { + /** + Constructs this object with a message equal to all arguments + concatenated with a space between each one. + */ constructor(...args){ - super(...args); + super(args.join(' ')); this.name = 'SQLite3Error'; } }; - const toss3 = (...args)=>{throw new SQLite3Error(args)}; + const toss3 = (...args)=>{throw new SQLite3Error(...args)}; sqlite3.SQLite3Error = SQLite3Error; /** @@ -195,7 +199,7 @@ }; /** - Expects to be passed (arguments) from DB.exec() and + Expects to be passed the `arguments` object from DB.exec() and DB.execMulti(). Does the argument processing/validation, throws on error, and returns a new object on success: @@ -842,26 +846,47 @@ /** Starts a transaction, calls the given callback, and then either - rolls back or commits the transaction, depending on whether the - callback throw. The callback is pass this db object as its only - argument. On success, returns the result of the callback. - Throws on error. + rolls back or commits the savepoint, depending on whether the + callback throws. The callback is passed this db object as its + only argument. On success, returns the result of the + callback. Throws on error. + + Note that transactions may not be nested, so this will throw if + it is called recursively. For nested transactions, use the + savepoint() method or manually manage SAVEPOINTs using exec(). */ - callInTransaction: function(callback){ - affirmDbOpen(this); - let err, rc; - this.exec("BEGIN"); - try { rc = callback(this); } - catch(e){ - err = e; + transaction: function(callback){ + affirmDbOpen(this).exec("BEGIN"); + try { + const rc = callback(this); + this.exec("COMMIT"); + return rc; + }catch(e){ + this.exec("ROLLBACK"); throw e; - }finally{ - if(err) this.exec("ROLLBACK"); - else this.exec("COMMIT"); } - return rc; }, + /** + This works similarly to transaction() but uses sqlite3's SAVEPOINT + feature. This function starts a savepoint (with an unspecified name) + and calls the given callback function, passing it this db object. + If the callback returns, the savepoint is released (committed). If + the callback throws, the savepoint is rolled back. If it does not + throw, it returns the result of the callback. + */ + savepoint: function(callback){ + affirmDbOpen(this).exec("SAVEPOINT oo1"); + try { + const rc = callback(this); + this.exec("RELEASE oo1"); + return rc; + }catch(e){ + this.execMulti("ROLLBACK to SAVEPOINT oo1; RELEASE SAVEPOINT oo1"); + throw e; + } + }, + /** This function currently does nothing and always throws. It WILL BE REMOVED pending other refactoring, to eliminate a hard diff --git a/ext/wasm/demo-oo1.js b/ext/wasm/demo-oo1.js index 480ea4938f..1ea7e05c64 100644 --- a/ext/wasm/demo-oo1.js +++ b/ext/wasm/demo-oo1.js @@ -26,13 +26,9 @@ oo = sqlite3.oo1, wasm = capi.wasm; - // If we have persistent storage, maybe init and mount it: - const dbDir = true - ? "" // this demo works better without persistent storage. - : capi.sqlite3_web_persistent_dir(); - // ^^^ returns name of persistent mount point or "" if we have none - + const dbDir = 1 ? "" : capi.sqlite3_web_persistent_dir(); const db = new oo.DB(dbDir+"/mydb.sqlite3"); + log("db =",db.filename); /** Never(!) rely on garbage collection to clean up DBs and (especially) statements. Always wrap their lifetimes in @@ -164,14 +160,39 @@ } try { - db.callInTransaction( function(D) { + db.transaction( function(D) { D.exec("delete from t"); log("In transaction: count(*) from t =",db.selectValue("select count(*) from t")); - throw new sqlite3.SQLite3Error("Demonstrating callInTransaction() rollback"); + throw new sqlite3.SQLite3Error("Demonstrating transaction() rollback"); }); }catch(e){ if(e instanceof sqlite3.SQLite3Error){ - log("Got expected exception:",e.message); + log("Got expected exception from db.transaction():",e.message); + log("count(*) from t =",db.selectValue("select count(*) from t")); + }else{ + throw e; + } + } + + try { + db.savepoint( function(D) { + D.exec("delete from t"); + log("In savepoint: count(*) from t =",db.selectValue("select count(*) from t")); + D.savepoint(function(DD){ + const rows = []; + D.execMulti({ + sql: ["insert into t(a,b) values(99,100);", + "select count(*) from t"], + rowMode: 0, + resultRows: rows + }); + log("In nested savepoint. Row count =",rows[0]); + throw new sqlite3.SQLite3Error("Demonstrating nested savepoint() rollback"); + }) + }); + }catch(e){ + if(e instanceof sqlite3.SQLite3Error){ + log("Got expected exception from nested db.savepoint():",e.message); log("count(*) from t =",db.selectValue("select count(*) from t")); }else{ throw e; @@ -205,13 +226,11 @@ const runDemos = function(Module){ //log("Module",Module); const sqlite3 = Module.sqlite3, - capi = sqlite3.capi, - oo = sqlite3.oo1, - wasm = capi.wasm; + capi = sqlite3.capi; log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); log("sqlite3 namespace:",sqlite3); try { - [ demo1 ].forEach((f)=>f(sqlite3, Module)) + demo1(sqlite3); }catch(e){ error("Exception:",e.message); throw e; diff --git a/manifest b/manifest index 9601cb65da..c93efa4806 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improve\san\sexception\scheck\sin\sdemo-oo1.js. -D 2022-08-18T11:16:27.283 +C javascript:\srename\sand\ssimplify\sDB.callInTransaction()\sas\sDB.transaction().\sAdd\sDB.savepoint(),\swhich\sworks\sthe\ssame\sas\stransaction()\sbut\suses\ssavepoints.\sCorrect\sconcatenation\sof\sarguments\sto\sSQLite3Error\sto\suse\sspaces\sinstead\sof\scommas.\sTest\sthat\sdemo-oo1.js\sworks\swith\spersistent\sOPFS\sstorage\s(output\sdiffers\sdue\sto\spersistent\srows,\sbut\sthe\sdemo\sworks). +D 2022-08-18T12:21:58.613 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -483,7 +483,7 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 149fd63a0400cd1d69548887ffde2ed89c13283384a63c2e9fcfc695e38a9e11 F ext/wasm/api/sqlite3-api-glue.js 4a09dd1153874f7a716cf953329bc1e78bf24e0870a9aad15214ffd51ac913bc -F ext/wasm/api/sqlite3-api-oo1.js d68dc7a692bc54385d9b851e914b386a146dc6c86624bfeb4672280a2d822082 +F ext/wasm/api/sqlite3-api-oo1.js 6c14e97987fafdd5f63ffa8a086e5316d49c115743add4dabfacb302a7c76b45 F ext/wasm/api/sqlite3-api-opfs.js c93cdd14f81a26b3a64990515ee05c7e29827fbc8fba4e4c2fef3a37a984db89 F ext/wasm/api/sqlite3-api-prologue.js 96997e411b41ff3d4024c2ad625c5cdb7b6a619ba8beece4662ad190191b1119 F ext/wasm/api/sqlite3-api-worker1.js 74130ec4979baeaf3e909c7619b99e9f466e2d816cd07370987ff639d168ef45 @@ -494,7 +494,7 @@ F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 F ext/wasm/common/whwasmutil.js 41b8e097e0a9cb07c24c0ede3c81b72470a63f4a4efb07f75586dc131569f5ae F ext/wasm/demo-oo1.html 75646855b38405d82781246fd08c852a2b3bee05dd9f0fe10ab655a8cffb79aa -F ext/wasm/demo-oo1.js 09c2e3f8dab87308d945da51eef4dae206f1e0a4edc6d2207096b5617a64b651 +F ext/wasm/demo-oo1.js 8be9c6be3c8e579eab4e7a5ee720ed122e38275a1f105169c6826193e42cf102 F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 @@ -2006,8 +2006,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f5059ee6f9fc55a381cbf08a30dfb9a5636c0b44341e42f4e9f12a3b109b5507 -R 26de05b21625601bc9d1934cb0717538 +P 55e1b775fa7ec492817cc847036df7794c3c22b78558e9e9c513f24c775dedab +R c7dd436c51b79816d21badfd7965a0c6 U stephan -Z 631a9c2307319184f6094a1100613815 +Z 56fa2cd60f3999351a57836a87a9c295 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9d14975f11..6c09520662 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -55e1b775fa7ec492817cc847036df7794c3c22b78558e9e9c513f24c775dedab \ No newline at end of file +e8c323f12b6223bc9dcbbec40698969c7917128aa50cf599c247df23f607ae61 \ No newline at end of file From 4ff42a82f79ac8224ec7946714ee0c199869915c Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 18 Aug 2022 15:53:27 +0000 Subject: [PATCH 018/428] Add notes to ext/wasm/README.md explaining how to run tests on a remote machine. FossilOrigin-Name: 7a3c444fb515413254b426908e4d3528ccc664a629628c23b7b85bd21c060d0e --- ext/wasm/README.md | 27 +++++++++++++++++++++++++++ manifest | 14 +++++++------- manifest.uuid | 2 +- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/ext/wasm/README.md b/ext/wasm/README.md index 1702e1f427..f950013597 100644 --- a/ext/wasm/README.md +++ b/ext/wasm/README.md @@ -10,6 +10,7 @@ below for Linux environments: ``` # Clone the emscripten repository: +$ sudo apt install git $ git clone https://github.com/emscripten-core/emsdk.git $ cd emsdk @@ -74,6 +75,32 @@ 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. +# Testing on a remote machine that is accessed via SSH + +*NB: The following are developer notes, last validated on 2022-08-18* + + * Remote: Install git, emsdk, and althttpd + * Use a [version of althttpd](https://sqlite.org/althttpd/timeline?r=enable-atomics) + that adds HTTP reply header lines to enable SharedArrayBuffers. These header + lines are required: +``` + Cross-Origin-Opener-Policy: same-origin + Cross-Origin-Embedder-Policy: require-corp +``` + * Remote: Install the SQLite source tree. CD to ext/wasm + * Remote: "`make`" to build WASM + * Remote: althttpd --port 8080 --popup + * Local: ssh -L 8180:localhost:8080 remote + * Local: Point your web-browser at http://localhost:8180/testing1.html + +In order to enable [SharedArrayBuffers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer), +the web-browser requires that the two extra Cross-Origin lines be present +in HTTP reply headers and that the request must come from "localhost". +Since the web-server is on a different machine from +the web-broser, the localhost requirement means that the connection must be tunneled +using SSH. + + # Known Quirks and Limitations diff --git a/manifest b/manifest index c93efa4806..6b9cdd5783 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C javascript:\srename\sand\ssimplify\sDB.callInTransaction()\sas\sDB.transaction().\sAdd\sDB.savepoint(),\swhich\sworks\sthe\ssame\sas\stransaction()\sbut\suses\ssavepoints.\sCorrect\sconcatenation\sof\sarguments\sto\sSQLite3Error\sto\suse\sspaces\sinstead\sof\scommas.\sTest\sthat\sdemo-oo1.js\sworks\swith\spersistent\sOPFS\sstorage\s(output\sdiffers\sdue\sto\spersistent\srows,\sbut\sthe\sdemo\sworks). -D 2022-08-18T12:21:58.613 +C Add\snotes\sto\sext/wasm/README.md\sexplaining\show\sto\srun\stests\son\sa\sremote\nmachine. +D 2022-08-18T15:53:27.160 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -475,7 +475,7 @@ F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865 F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 F ext/wasm/GNUmakefile 19c21ce4df5583278cc495bffbe03c69dc64af60fa9dac01766d0192bd191ac6 -F ext/wasm/README.md 4b00ae7c7d93c4591251245f0996a319e2651361013c98d2efb0b026771b7331 +F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 F ext/wasm/api/README.md d876597edd2b9542b6ea031adaaff1c042076fde7b670b1dc6d8a87b28a6631b @@ -2006,8 +2006,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 55e1b775fa7ec492817cc847036df7794c3c22b78558e9e9c513f24c775dedab -R c7dd436c51b79816d21badfd7965a0c6 -U stephan -Z 56fa2cd60f3999351a57836a87a9c295 +P e8c323f12b6223bc9dcbbec40698969c7917128aa50cf599c247df23f607ae61 +R 391fcad47fcff47ace5e5b589a55467a +U drh +Z 306b6860fc24abe35b9649e8ac4acacb # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6c09520662..0d42128ad7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e8c323f12b6223bc9dcbbec40698969c7917128aa50cf599c247df23f607ae61 \ No newline at end of file +7a3c444fb515413254b426908e4d3528ccc664a629628c23b7b85bd21c060d0e \ No newline at end of file From 64d04a8d9fc15496ebab111972f4b3ae1578148a Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 22 Aug 2022 08:55:10 +0000 Subject: [PATCH 019/428] wasm: accommodated a JS API rename. FossilOrigin-Name: 00991335c4dae56232e999398e5e82d8161903ba7d084b16a73a150e83f1f782 --- ext/wasm/scratchpad-opfs-main.js | 2 +- ext/wasm/scratchpad-opfs-worker2.js | 2 +- manifest | 16 ++++++++-------- manifest.uuid | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ext/wasm/scratchpad-opfs-main.js b/ext/wasm/scratchpad-opfs-main.js index 8e9d123584..9f7d3fb1d8 100644 --- a/ext/wasm/scratchpad-opfs-main.js +++ b/ext/wasm/scratchpad-opfs-main.js @@ -25,7 +25,7 @@ const test1 = function(db){ db.exec("create table if not exists t(a);") - .callInTransaction(function(db){ + .transaction(function(db){ db.prepare("insert into t(a) values(?)") .bind(new Date().getTime()) .stepFinalize(); diff --git a/ext/wasm/scratchpad-opfs-worker2.js b/ext/wasm/scratchpad-opfs-worker2.js index 2e7527adc7..e27fe2b372 100644 --- a/ext/wasm/scratchpad-opfs-worker2.js +++ b/ext/wasm/scratchpad-opfs-worker2.js @@ -38,7 +38,7 @@ const test1 = function(db){ db.execMulti("create table if not exists t(a);") - .callInTransaction(function(db){ + .transaction(function(db){ db.prepare("insert into t(a) values(?)") .bind(new Date().getTime()) .stepFinalize(); diff --git a/manifest b/manifest index 6b9cdd5783..9faf2e216e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\snotes\sto\sext/wasm/README.md\sexplaining\show\sto\srun\stests\son\sa\sremote\nmachine. -D 2022-08-18T15:53:27.160 +C wasm:\saccommodated\sa\sJS\sAPI\srename. +D 2022-08-22T08:55:10.642 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -504,10 +504,10 @@ F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356a F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19 F ext/wasm/scratchpad-opfs-main.html 4565cf194e66188190d35f70e82553e2e2d72b9809b73c94ab67b8cfd14d2e0c -F ext/wasm/scratchpad-opfs-main.js a819ed26047c5539630cea59add6a5082ba04cdf82da2df2e0707d4d69af6cb1 +F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 -F ext/wasm/scratchpad-opfs-worker2.js de3ea77548243a638c8426b7e43cc1dbfc511013228ab98436eb102923ed6689 +F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0 F ext/wasm/sqlite3-worker1.js e93fe8e5da7cb56dcf4d1bb0aa44bf681b509e1c56f2a75885c0f408f034c42b F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409 F ext/wasm/testing1.js 9a97a7e45ce122b479b4a706ae1024abded67fd5f34a5764e41ff5efde8dfa17 @@ -2006,8 +2006,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e8c323f12b6223bc9dcbbec40698969c7917128aa50cf599c247df23f607ae61 -R 391fcad47fcff47ace5e5b589a55467a -U drh -Z 306b6860fc24abe35b9649e8ac4acacb +P 7a3c444fb515413254b426908e4d3528ccc664a629628c23b7b85bd21c060d0e +R 763c38437f86fddfab452c17546273d5 +U stephan +Z 9e445c1e5e3ac9504605b874a5870c61 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0d42128ad7..f81e130264 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7a3c444fb515413254b426908e4d3528ccc664a629628c23b7b85bd21c060d0e \ No newline at end of file +00991335c4dae56232e999398e5e82d8161903ba7d084b16a73a150e83f1f782 \ No newline at end of file From e3cd67603dc30d00e998bc9fb3183d49f43b830b Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 22 Aug 2022 13:34:13 +0000 Subject: [PATCH 020/428] Refactor JS API amalgamation such that the bootstrapping/configuration is deferred until the whole amalgamation is available, to facilitate providing clients with a way to initialize the API with their own config (noting that we're still one small level of refactoring away from being able to actually do that). FossilOrigin-Name: 9dbe9a6aecec43b51057375ef1d2d632db0d17eac8b7552c20cc91fc2f1a55d1 --- ext/wasm/api/sqlite3-api-cleanup.js | 68 +++++++----- ext/wasm/api/sqlite3-api-glue.js | 23 +---- ext/wasm/api/sqlite3-api-oo1.js | 8 +- ext/wasm/api/sqlite3-api-opfs.js | 9 +- ext/wasm/api/sqlite3-api-prologue.js | 149 +++++++++++++++++++++------ ext/wasm/api/sqlite3-api-worker1.js | 7 +- ext/wasm/api/sqlite3-wasm.c | 23 +++-- manifest | 24 ++--- manifest.uuid | 2 +- 9 files changed, 206 insertions(+), 107 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-cleanup.js b/ext/wasm/api/sqlite3-api-cleanup.js index a2f921a5d7..ce24ad0137 100644 --- a/ext/wasm/api/sqlite3-api-cleanup.js +++ b/ext/wasm/api/sqlite3-api-cleanup.js @@ -16,29 +16,45 @@ various subsystems. */ 'use strict'; -self.sqlite3.postInit.forEach( - self.importScripts/*global is a Worker*/ - ? function(f){ - /** We try/catch/report for the sake of failures which happen in - a Worker, as those exceptions can otherwise get completely - swallowed, leading to confusing downstream errors which have - nothing to do with this failure. */ - try{ f(self, self.sqlite3) } - catch(e){ - console.error("Error in postInit() function:",e); - throw e; - } - } - : (f)=>f(self, self.sqlite3) -); -delete self.sqlite3.postInit; -if(self.location && +self.location.port > 1024){ - console.warn("Installing sqlite3 bits as global S for dev-testing purposes."); - self.S = self.sqlite3; -} -/* Clean up temporary global-scope references to our APIs... */ -self.sqlite3.config.Module.sqlite3 = self.sqlite3 -/* ^^^^ Currently needed by test code and Worker API setup */; -delete self.sqlite3.capi.util /* arguable, but these are (currently) internal-use APIs */; -delete self.sqlite3 /* clean up our global-scope reference */; -//console.warn("Module.sqlite3 =",Module.sqlite3); +(function(){ + /** + Replace sqlite3ApiBootstrap() with a variant which plugs in the + Emscripten-based config for all config options which the client + does not provide. + */ + const SAB = self.sqlite3ApiBootstrap; + self.sqlite3ApiBootstrap = function(apiConfig){ + apiConfig = apiConfig||{}; + const configDefaults = { + Module: Module /* ==> Emscripten-style Module object. Currently + needs to be exposed here for test code. NOT part + of the public API. */, + exports: Module['asm'], + memory: Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */ + }; + const config = {}; + Object.keys(configDefaults).forEach(function(k){ + config[k] = Object.prototype.hasOwnProperty.call(apiConfig, k) + ? apiConfig[k] : configDefaults[k]; + }); + return SAB(config); + }; + + /** + For current (2022-08-22) purposes, automatically call sqlite3ApiBootstrap(). + That decision will be revisited at some point, as we really want client code + to be able to call this to configure certain parts. + */ + const sqlite3 = self.sqlite3ApiBootstrap(); + + if(self.location && +self.location.port > 1024){ + console.warn("Installing sqlite3 bits as global S for dev-testing purposes."); + self.S = sqlite3; + } + + /* Clean up temporary references to our APIs... */ + delete self.sqlite3ApiBootstrap; + Module.sqlite3 = sqlite3 /* Currently needed by test code */; + delete sqlite3.capi.util /* arguable, but these are (currently) internal-use APIs */; + //console.warn("Module.sqlite3 =",Module.sqlite3); +})(); diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index efcd6fea9b..84bbdb10ac 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -16,23 +16,9 @@ initializes the main API pieces so that the downstream components (e.g. sqlite3-api-oo1.js) have all that they need. */ -(function(self){ +self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ 'use strict'; const toss = (...args)=>{throw new Error(args.join(' '))}; - - self.sqlite3 = self.sqlite3ApiBootstrap({ - Module: Module /* ==> Emscripten-style Module object. Currently - needs to be exposed here for test code. NOT part - of the public API. */, - exports: Module['asm'], - memory: Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */, - bigIntEnabled: !!self.BigInt64Array, - allocExportName: 'malloc', - deallocExportName: 'free' - }); - delete self.sqlite3ApiBootstrap; - - const sqlite3 = self.sqlite3; const capi = sqlite3.capi, wasm = capi.wasm, util = capi.util; self.WhWasmUtilInstaller(capi.wasm); delete self.WhWasmUtilInstaller; @@ -57,7 +43,7 @@ return oldP(v); }; wasm.xWrap.argAdapter('.pointer', adapter); - } + } /* ".pointer" xWrap() argument adapter */ // WhWasmUtil.xWrap() bindings... { @@ -78,7 +64,7 @@ capi[e[0]] = wasm.xWrap.apply(null, e); } - /* For functions which cannot work properly unless + /* For C API functions which cannot work properly unless wasm.bigIntEnabled is true, install a bogus impl which throws if called when bigIntEnabled is false. */ const fI64Disabled = function(fname){ @@ -198,5 +184,4 @@ capi[s.name] = sqlite3.StructBinder(s); } } - -})(self); +}); diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 0e04b63ebf..be9d8af5a8 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -14,10 +14,9 @@ WASM build. It requires that sqlite3-api-glue.js has already run and it installs its deliverable as self.sqlite3.oo1. */ -(function(self){ +self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const toss = (...args)=>{throw new Error(args.join(' '))}; - const sqlite3 = self.sqlite3 || toss("Missing main sqlite3 object."); const capi = sqlite3.capi, util = capi.util; /* What follows is colloquially known as "OO API #1". It is a binding of the sqlite3 API which is designed to be run within @@ -1547,5 +1546,6 @@ }, DB, Stmt - }/*SQLite3 object*/; -})(self); + }/*oo1 object*/; +}); + diff --git a/ext/wasm/api/sqlite3-api-opfs.js b/ext/wasm/api/sqlite3-api-opfs.js index 4acab7770a..693432b35a 100644 --- a/ext/wasm/api/sqlite3-api-opfs.js +++ b/ext/wasm/api/sqlite3-api-opfs.js @@ -31,12 +31,13 @@ // FileSystemDirectoryHandle // FileSystemFileHandle // FileSystemFileHandle.prototype.createSyncAccessHandle -self.sqlite3.postInit.push(function(self, sqlite3){ +self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const warn = console.warn.bind(console), error = console.error.bind(console); - if(!self.importScripts || !self.FileSystemFileHandle - || !self.FileSystemFileHandle.prototype.createSyncAccessHandle){ - warn("OPFS not found or its sync API is not available in this environment."); + if(!self.importScripts || !self.FileSystemFileHandle){ + //|| !self.FileSystemFileHandle.prototype.createSyncAccessHandle){ + // ^^^ sync API is not required with WASMFS/OPFS backend. + warn("OPFS is not available in this environment."); return; }else if(!sqlite3.capi.wasm.bigIntEnabled){ error("OPFS requires BigInt support but sqlite3.capi.wasm.bigIntEnabled is false."); diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index cf9fd896a6..87cc40b410 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -78,25 +78,88 @@ */ /** - This global symbol is is only a temporary measure: the JS-side - post-processing will remove that object from the global scope when - setup is complete. We require it there temporarily in order to glue - disparate parts together during the loading of the API (which spans - several components). + sqlite3ApiBootstrap() is the only global symbol exposed by this + API. It is intended to be called one time at the end of the API + amalgamation process, passed configuration details for the current + environment, and then optionally be removed from the global object + using `delete self.sqlite3ApiBootstrap`. - This function requires a configuration object intended to abstract + This function expects a configuration object, intended to abstract away details specific to any given WASM environment, primarily so - that it can be used without any _direct_ dependency on Emscripten. - (That said, OO API #1 requires, as of this writing, Emscripten's - virtual filesystem API. Baby steps.) + that it can be used without any _direct_ dependency on + Emscripten. The config object is only honored the first time this + is called. Subsequent calls ignore the argument and return the same + (configured) object which gets initialized by the first call. + + The config object properties include: + + - `Module`: Emscripten-style module object. Currently only required + by certain test code and is _not_ part of the public interface. + (TODO: rename this to EmscriptenModule to be more explicit.) + + - `exports`: the "exports" object for the current WASM + environment. In an Emscripten build, this should be set to + `Module['asm']`. + + - `memory`: optional WebAssembly.Memory object, defaulting to + `exports.memory`. In Emscripten environments this should be set + to `Module.wasmMemory` if the build uses `-sIMPORT_MEMORY`, or be + left undefined/falsy to default to `exports.memory` when using + WASM-exported memory. + + - `bigIntEnabled`: true if BigInt support is enabled. Defaults to + true if self.BigInt64Array is available, else false. Some APIs + will throw exceptions if called without BigInt support, as BigInt + is required for marshalling C-side int64 into and out of JS. + + - `allocExportName`: the name of the function, in `exports`, of the + `malloc(3)`-compatible routine for the WASM environment. Defaults + to `"malloc"`. + + - `deallocExportName`: the name of the function, in `exports`, of + the `free(3)`-compatible routine for the WASM + environment. Defaults to `"free"`. + + - `persistentDirName`: if the environment supports persistent storage, this + directory names the "mount point" for that directory. It must be prefixed + by `/` and may currently contain only a single directory-name part. Using + the root directory name is not supported by any current persistent backend. */ -self.sqlite3ApiBootstrap = function(config){ +self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(apiConfig){ 'use strict'; + + if(sqlite3ApiBootstrap.sqlite3){ /* already initalized */ + console.warn("sqlite3ApiBootstrap() called multiple times.", + "Config and external initializers are ignored on calls after the first."); + return sqlite3ApiBootstrap.sqlite3; + } + + apiConfig = apiConfig||{}; + const config = Object.create(null); + { + const configDefaults = { + Module: undefined/*needed for some test code, not part of the public API*/, + exports: undefined, + memory: undefined, + bigIntEnabled: !!self.BigInt64Array, + allocExportName: 'malloc', + deallocExportName: 'free', + persistentDirName: '/persistent' + }; + Object.keys(configDefaults).forEach(function(k){ + config[k] = Object.prototype.hasOwnProperty.call(apiConfig, k) + ? apiConfig[k] : configDefaults[k]; + }); + } /** Throws a new Error, the message of which is the concatenation all args with a space between each. */ const toss = (...args)=>{throw new Error(args.join(' '))}; + if(config.persistentDirName && !/^\/[^/]+$/.test(config.persistentDirName)){ + toss("config.persistentDirName must be falsy or in the form '/dir-name'."); + } + /** Returns true if n is a 32-bit (signed) integer, else false. This is used for determining when we need to switch to @@ -143,7 +206,18 @@ self.sqlite3ApiBootstrap = function(config){ }; const utf8Decoder = new TextDecoder('utf-8'); - const typedArrayToString = (str)=>utf8Decoder.decode(str); + + /** Internal helper to use in operations which need to distinguish + between SharedArrayBuffer heap memory and non-shared heap. */ + const __SAB = ('undefined'===typeof SharedArrayBuffer) + ? function(){} : SharedArrayBuffer; + const typedArrayToString = function(arrayBuffer, begin, end){ + return utf8Decoder.decode( + (arrayBuffer.buffer instanceof __SAB) + ? arrayBuffer.slice(begin, end) + : arrayBuffer.subarray(begin, end) + ); + }; /** An Error subclass specifically for reporting Wasm-level malloc() @@ -591,9 +665,6 @@ self.sqlite3ApiBootstrap = function(config){ TODOs and caveats: - - The directory name (mount point) for persistent storage is - currently hard-coded. It needs to be configurable. - - If persistent storage is available at the root of the virtual filesystem, this interface cannot currently distinguish that from the lack of persistence. That case cannot currently (with @@ -604,12 +675,17 @@ self.sqlite3ApiBootstrap = function(config){ capi.sqlite3_web_persistent_dir = function(){ if(undefined !== __persistentDir) return __persistentDir; // If we have no OPFS, there is no persistent dir - if(!self.FileSystemHandle || !self.FileSystemDirectoryHandle + const pdir = config.persistentDirName; + if(!pdir + || !self.FileSystemHandle + || !self.FileSystemDirectoryHandle || !self.FileSystemFileHandle){ return __persistentDir = ""; } try{ - if(0===this.wasm.xCall('sqlite3_wasm_init_opfs')){ + if(pdir && 0===this.wasm.xCallWrapped( + 'sqlite3_wasm_init_opfs', 'i32', ['string'], pdir + )){ /** OPFS does not support locking and will trigger errors if we try to lock. We don't _really_ want to _unconditionally_ install a non-locking sqlite3 VFS as the @@ -617,13 +693,12 @@ self.sqlite3ApiBootstrap = function(config){ time being. That said: locking is a no-op on all of the current WASM storage, so this isn't (currently) as bad as it may initially seem. */ - const pVfs = this.sqlite3_vfs_find("unix-none"); + const pVfs = sqlite3.capi.sqlite3_vfs_find("unix-none"); if(pVfs){ - this.sqlite3_vfs_register(pVfs,1); - //warn("Installed 'unix-none' as the default sqlite3 VFS."); + capi.sqlite3_vfs_register(pVfs,1); + console.warn("Installed 'unix-none' as the default sqlite3 VFS."); } - return __persistentDir = - "/persistent" /* name is hard-coded in sqlite3_wasm_init_opfs()!*/; + return __persistentDir = pdir; }else{ return __persistentDir = ""; } @@ -644,19 +719,29 @@ self.sqlite3ApiBootstrap = function(config){ }.bind(capi); /* The remainder of the API will be set up in later steps. */ - return { - /** - An Error subclass which is thrown by this.wasm.alloc() on OOM. - */ + const sqlite3 = { WasmAllocError: WasmAllocError, capi, - postInit: [ - /* some pieces of the API may install functions into this array, - and each such function will be called, passed (self,sqlite3), - at the very end of the API load/init process, where self is - the current global object and sqlite3 is the object returned - from sqlite3ApiBootstrap(). This array will be removed at the - end of the API setup process. */], config }; + sqlite3ApiBootstrap.initializers.forEach((f)=>f(sqlite3)); + delete sqlite3ApiBootstrap.initializers; + sqlite3ApiBootstrap.sqlite3 = sqlite3; + return sqlite3; }/*sqlite3ApiBootstrap()*/; +/** + self.sqlite3ApiBootstrap.initializers is an internal detail used by + the various pieces of the sqlite3 API's amalgamation process. It + must not be modified by client code except when plugging such code + into the amalgamation process. + + Each component of the amalgamation is expected to append a function + to this array. When sqlite3ApiBootstrap() is called for the first + time, each such function will be called (in their appended order) + and passed the sqlite3 namespace object, into which they can install + their features (noting that most will also require that certain + features alread have been installed). At the end of that process, + this array is deleted. +*/ +self.sqlite3ApiBootstrap.initializers = []; +self.sqlite3ApiBootstrap.sqlite3 = undefined /* installed at first call */; diff --git a/ext/wasm/api/sqlite3-api-worker1.js b/ext/wasm/api/sqlite3-api-worker1.js index 565946bbc6..a7b759851f 100644 --- a/ext/wasm/api/sqlite3-api-worker1.js +++ b/ext/wasm/api/sqlite3-api-worker1.js @@ -43,7 +43,8 @@ In some contexts, however, listening for the above message is a better fit. */ -self.sqlite3.initWorker1API = function(){ +self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ +sqlite3.initWorker1API = function(){ 'use strict'; /** UNDER CONSTRUCTION @@ -418,4 +419,6 @@ self.sqlite3.initWorker1API = function(){ wState.post(evType, response, wMsgHandler.xfer); }; setTimeout(()=>self.postMessage({type:'sqlite3-api',data:'worker1-ready'}), 0); -}.bind({self, sqlite3: self.sqlite3}); +}.bind({self, sqlite3}); +}); + diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index 487baecf10..070282b8ee 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -435,7 +435,8 @@ int sqlite3_wasm_vfs_unlink(const char * zName){ #include /* ** This function is NOT part of the sqlite3 public API. It is strictly -** for use by the sqlite project's own JS/WASM bindings. +** for use by the sqlite project's own JS/WASM bindings, specifically +** only when building with Emscripten's WASMFS support. ** ** This function should only be called if the JS side detects the ** existence of the Origin-Private FileSystem (OPFS) APIs in the @@ -443,14 +444,19 @@ int sqlite3_wasm_vfs_unlink(const char * zName){ ** WASMFS backend impl for OPFS. On success, subsequent calls are ** no-ops. ** +** This function may be passed a "mount point" name, which must have a +** leading "/" and is currently restricted to a single path component, +** e.g. "/foo" is legal but "/foo/" and "/foo/bar" are not. If it is +** NULL or empty, it defaults to "/persistent". +** ** Returns 0 on success, SQLITE_NOMEM if instantiation of the backend -** object fails, SQLITE_IOERR if mkdir() of the "/persistent" dir in +** object fails, SQLITE_IOERR if mkdir() of the zMountPoint dir in ** the virtual FS fails. In builds compiled without SQLITE_WASM_OPFS ** defined, SQLITE_NOTFOUND is returned without side effects. */ -int sqlite3_wasm_init_opfs(void){ +int sqlite3_wasm_init_opfs(const char *zMountPoint){ static backend_t pOpfs = 0; - static const char * zDir = "/persistent"; + if( !zMountPoint || !*zMountPoint ) zMountPoint = "/persistent"; if( !pOpfs ){ pOpfs = wasmfs_create_opfs_backend(); if( pOpfs ){ @@ -459,12 +465,15 @@ int sqlite3_wasm_init_opfs(void){ } /** It's not enough to instantiate the backend. We have to create a mountpoint in the VFS and attach the backend to it. */ - if( pOpfs && 0!=access(zDir, F_OK) ){ + if( pOpfs && 0!=access(zMountPoint, F_OK) ){ /* mkdir() simply hangs when called from fiddle app. Cause is not yet determined but the hypothesis is an init-order issue. */ - const int rc = wasmfs_create_directory(zDir, 0777, pOpfs); - emscripten_console_log(rc ? "OPFS mkdir failed." : "OPFS mkdir ok."); + /* Note that this check and is not robust but it will + hypothetically suffice for the transient wasm-based virtual + filesystem we're currently running in. */ + const int rc = wasmfs_create_directory(zMountPoint, 0777, pOpfs); + emscripten_console_logf("OPFS mkdir rc=%d", rc); if(rc) return SQLITE_IOERR; } return pOpfs ? 0 : SQLITE_NOMEM; diff --git a/manifest b/manifest index 9faf2e216e..8ae71848be 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C wasm:\saccommodated\sa\sJS\sAPI\srename. -D 2022-08-22T08:55:10.642 +C Refactor\sJS\sAPI\samalgamation\ssuch\sthat\sthe\sbootstrapping/configuration\sis\sdeferred\suntil\sthe\swhole\samalgamation\sis\savailable,\sto\sfacilitate\sproviding\sclients\swith\sa\sway\sto\sinitialize\sthe\sAPI\swith\stheir\sown\sconfig\s(noting\sthat\swe're\sstill\sone\ssmall\slevel\sof\srefactoring\saway\sfrom\sbeing\sable\sto\sactually\sdo\sthat). +D 2022-08-22T13:34:13.519 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -481,14 +481,14 @@ F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de F ext/wasm/api/README.md d876597edd2b9542b6ea031adaaff1c042076fde7b670b1dc6d8a87b28a6631b F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b -F ext/wasm/api/sqlite3-api-cleanup.js 149fd63a0400cd1d69548887ffde2ed89c13283384a63c2e9fcfc695e38a9e11 -F ext/wasm/api/sqlite3-api-glue.js 4a09dd1153874f7a716cf953329bc1e78bf24e0870a9aad15214ffd51ac913bc -F ext/wasm/api/sqlite3-api-oo1.js 6c14e97987fafdd5f63ffa8a086e5316d49c115743add4dabfacb302a7c76b45 -F ext/wasm/api/sqlite3-api-opfs.js c93cdd14f81a26b3a64990515ee05c7e29827fbc8fba4e4c2fef3a37a984db89 -F ext/wasm/api/sqlite3-api-prologue.js 96997e411b41ff3d4024c2ad625c5cdb7b6a619ba8beece4662ad190191b1119 -F ext/wasm/api/sqlite3-api-worker1.js 74130ec4979baeaf3e909c7619b99e9f466e2d816cd07370987ff639d168ef45 +F ext/wasm/api/sqlite3-api-cleanup.js eee5ac931fa0aee2cace52a0dff0cf22ae56a1993a88d911bd0dd384fb380c9a +F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3 +F ext/wasm/api/sqlite3-api-oo1.js f6dcaac3270182471f97efcfda25bd4a4ac1777b8ec52ebd1c6846721160e54c +F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 +F ext/wasm/api/sqlite3-api-prologue.js 5d1b13b23af48ce952e30a0f2d6dff4bc4b33f2dc36fdcaf69c164fd9a72b60f +F ext/wasm/api/sqlite3-api-worker1.js ceb1fc88d8a3742c069632e88fd05c14d5a79eb86bdb9e12969ec37f64fbf42b F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 -F ext/wasm/api/sqlite3-wasm.c 0e78035045e3328fb050ec9580c6bfb714c756a1d3b917259e58baf9b0332c98 +F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 F ext/wasm/common/SqliteTestUtil.js e41a1406f18da9224523fad0c48885caf995b56956a5b9852909c0989e687e90 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 @@ -2006,8 +2006,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 7a3c444fb515413254b426908e4d3528ccc664a629628c23b7b85bd21c060d0e -R 763c38437f86fddfab452c17546273d5 +P 00991335c4dae56232e999398e5e82d8161903ba7d084b16a73a150e83f1f782 +R d8e7cfc53479bc2a5e5d34dd2cc26aa3 U stephan -Z 9e445c1e5e3ac9504605b874a5870c61 +Z dd954bd42edcc1a06b3b31bdffb215a4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f81e130264..174e4d0afc 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -00991335c4dae56232e999398e5e82d8161903ba7d084b16a73a150e83f1f782 \ No newline at end of file +9dbe9a6aecec43b51057375ef1d2d632db0d17eac8b7552c20cc91fc2f1a55d1 \ No newline at end of file From ae708b2b018ac558b2f5c7626269415c86e85382 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 22 Aug 2022 21:37:17 +0000 Subject: [PATCH 021/428] More experimentation with how client-side config state can be passed on to initialize the JS-side sqlite3 API. FossilOrigin-Name: ea2acc454c012a62556f6d0623d6eff60736d24aa214a64462b423623ef44d47 --- ext/wasm/api/sqlite3-api-cleanup.js | 38 ++++++++++++++++++---------- ext/wasm/api/sqlite3-api-prologue.js | 10 ++++++-- ext/wasm/common/SqliteTestUtil.js | 27 ++++++++++++++++++++ ext/wasm/demo-oo1.js | 14 +++++++--- ext/wasm/testing1.js | 8 +----- manifest | 20 +++++++-------- manifest.uuid | 2 +- 7 files changed, 81 insertions(+), 38 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-cleanup.js b/ext/wasm/api/sqlite3-api-cleanup.js index ce24ad0137..d989faccaf 100644 --- a/ext/wasm/api/sqlite3-api-cleanup.js +++ b/ext/wasm/api/sqlite3-api-cleanup.js @@ -11,12 +11,12 @@ *********************************************************************** This file is the tail end of the sqlite3-api.js constellation, - intended to be appended after all other files so that it can clean - up any global systems temporarily used for setting up the API's - various subsystems. + intended to be appended after all other sqlite3-api-*.js files so + that it can finalize any setup and clean up any global symbols + temporarily used for setting up the API's various subsystems. */ 'use strict'; -(function(){ +if('undefined' !== typeof Module){ // presumably an Emscripten build /** Replace sqlite3ApiBootstrap() with a variant which plugs in the Emscripten-based config for all config options which the client @@ -24,7 +24,7 @@ */ const SAB = self.sqlite3ApiBootstrap; self.sqlite3ApiBootstrap = function(apiConfig){ - apiConfig = apiConfig||{}; + apiConfig = apiConfig || {}; const configDefaults = { Module: Module /* ==> Emscripten-style Module object. Currently needs to be exposed here for test code. NOT part @@ -34,18 +34,29 @@ }; const config = {}; Object.keys(configDefaults).forEach(function(k){ - config[k] = Object.prototype.hasOwnProperty.call(apiConfig, k) + config[k] = Object.getOwnPropertyDescriptor(apiConfig, k) ? apiConfig[k] : configDefaults[k]; }); + // Copy over any properties apiConfig defines but configDefaults does not... + Object.keys(apiConfig).forEach(function(k){ + if(!Object.getOwnPropertyDescriptor(config, k)){ + config[k] = apiConfig[k]; + } + }); return SAB(config); }; /** - For current (2022-08-22) purposes, automatically call sqlite3ApiBootstrap(). - That decision will be revisited at some point, as we really want client code - to be able to call this to configure certain parts. - */ - const sqlite3 = self.sqlite3ApiBootstrap(); + For current (2022-08-22) purposes, automatically call + sqlite3ApiBootstrap(). That decision will be revisited at some + point, as we really want client code to be able to call this to + configure certain parts. If the global sqliteApiConfig property + is available, it is assumed to be a config object for + sqlite3ApiBootstrap(). + */ + //console.warn("self.sqlite3ApiConfig = ",self.sqlite3ApiConfig); + const sqlite3 = self.sqlite3ApiBootstrap(self.sqlite3ApiConfig || Object.create(null)); + delete self.sqlite3ApiBootstrap; if(self.location && +self.location.port > 1024){ console.warn("Installing sqlite3 bits as global S for dev-testing purposes."); @@ -53,8 +64,7 @@ } /* Clean up temporary references to our APIs... */ - delete self.sqlite3ApiBootstrap; - Module.sqlite3 = sqlite3 /* Currently needed by test code */; delete sqlite3.capi.util /* arguable, but these are (currently) internal-use APIs */; //console.warn("Module.sqlite3 =",Module.sqlite3); -})(); + Module.sqlite3 = sqlite3 /* Currently needed by test code and sqlite3-worker1.js */; +} diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 87cc40b410..1a38f0343a 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -134,7 +134,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(apiConfig){ return sqlite3ApiBootstrap.sqlite3; } - apiConfig = apiConfig||{}; + apiConfig = apiConfig || {}; const config = Object.create(null); { const configDefaults = { @@ -147,9 +147,15 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(apiConfig){ persistentDirName: '/persistent' }; Object.keys(configDefaults).forEach(function(k){ - config[k] = Object.prototype.hasOwnProperty.call(apiConfig, k) + config[k] = Object.getOwnPropertyDescriptor(apiConfig, k) ? apiConfig[k] : configDefaults[k]; }); + // Copy over any properties apiConfig defines but configDefaults does not... + Object.keys(apiConfig).forEach(function(k){ + if(!Object.getOwnPropertyDescriptor(config, k)){ + config[k] = apiConfig[k]; + } + }); } /** Throws a new Error, the message of which is the concatenation diff --git a/ext/wasm/common/SqliteTestUtil.js b/ext/wasm/common/SqliteTestUtil.js index c7c99240e6..4b4cefbf0c 100644 --- a/ext/wasm/common/SqliteTestUtil.js +++ b/ext/wasm/common/SqliteTestUtil.js @@ -168,6 +168,33 @@ } f.ui.status.classList.add('hidden'); } + }, + /** + Config options used by the Emscripten-dependent initialization + which happens via this.initSqlite3(). This object gets + (indirectly) passed to sqlite3ApiBootstrap() to configure the + sqlite3 API. + */ + sqlite3ApiConfig: { + persistentDirName: "/persistent" + }, + /** + Intended to be called by apps which need to call the + Emscripten-installed sqlite3InitModule() routine. This function + temporarily installs this.sqlite3ApiConfig into the self + object, calls it sqlite3InitModule(), and removes + self.sqlite3ApiConfig after initialization is done. Returns the + promise from sqlite3InitModule(), and the next then() handler + will get the Emscripten module object as its argument. That + module has the sqlite3's main namespace object installed as its + `sqlite3` property. + */ + initSqlite3: function(){ + self.sqlite3ApiConfig = this.sqlite3ApiConfig; + return self.sqlite3InitModule(this).then(function(M){ + delete self.sqlite3ApiConfig; + return M; + }); } }; })(self/*window or worker*/); diff --git a/ext/wasm/demo-oo1.js b/ext/wasm/demo-oo1.js index 1ea7e05c64..4eff51b8e8 100644 --- a/ext/wasm/demo-oo1.js +++ b/ext/wasm/demo-oo1.js @@ -26,8 +26,13 @@ oo = sqlite3.oo1, wasm = capi.wasm; - const dbDir = 1 ? "" : capi.sqlite3_web_persistent_dir(); - const db = new oo.DB(dbDir+"/mydb.sqlite3"); + const dbName = ( + 0 ? "" : capi.sqlite3_web_persistent_dir() + )+"/mydb.sqlite3" + if(0 && capi.sqlite3_web_persistent_dir()){ + capi.sqlite3_wasm_vfs_unlink(dbName); + } + const db = new oo.DB(dbName); log("db =",db.filename); /** Never(!) rely on garbage collection to clean up DBs and @@ -224,7 +229,7 @@ }/*demo1()*/; const runDemos = function(Module){ - //log("Module",Module); + //log("Module.sqlite3",Module); const sqlite3 = Module.sqlite3, capi = sqlite3.capi; log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); @@ -237,5 +242,6 @@ } }; - sqlite3InitModule(self.sqlite3TestModule).then(runDemos); + //self.sqlite3TestModule.sqlite3ApiConfig.persistentDirName = "/hi"; + self.sqlite3TestModule.initSqlite3().then(runDemos); })(); diff --git a/ext/wasm/testing1.js b/ext/wasm/testing1.js index 28f2b604b4..4144219527 100644 --- a/ext/wasm/testing1.js +++ b/ext/wasm/testing1.js @@ -1068,13 +1068,7 @@ log('capi.wasm.exports',capi.wasm.exports); }; - sqlite3InitModule(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); - // ^^^ Chrome warns: "VIOLATION: setTimeout() handler took A WHOLE 50ms!" + self.sqlite3TestModule.initSqlite3().then(function(theModule){ self._MODULE = theModule /* this is only to facilitate testing from the console */ runTests(theModule); }); diff --git a/manifest b/manifest index bab5e7c63d..b297210960 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sin\strunk. -D 2022-08-22T14:03:11.661 +C More\sexperimentation\swith\show\sclient-side\sconfig\sstate\scan\sbe\spassed\son\sto\sinitialize\sthe\sJS-side\ssqlite3\sAPI. +D 2022-08-22T21:37:17.339 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -481,20 +481,20 @@ F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de F ext/wasm/api/README.md d876597edd2b9542b6ea031adaaff1c042076fde7b670b1dc6d8a87b28a6631b F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b -F ext/wasm/api/sqlite3-api-cleanup.js eee5ac931fa0aee2cace52a0dff0cf22ae56a1993a88d911bd0dd384fb380c9a +F ext/wasm/api/sqlite3-api-cleanup.js acf798ce96285c0d52738466a96c9deb9d66647f711a40caecab90b5ce66ac3c F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3 F ext/wasm/api/sqlite3-api-oo1.js f6dcaac3270182471f97efcfda25bd4a4ac1777b8ec52ebd1c6846721160e54c F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 -F ext/wasm/api/sqlite3-api-prologue.js 5d1b13b23af48ce952e30a0f2d6dff4bc4b33f2dc36fdcaf69c164fd9a72b60f +F ext/wasm/api/sqlite3-api-prologue.js 6e0e7787ed955ea2b6158e0bb7608f63b54236847700d183e49e1f10d0525b8f F ext/wasm/api/sqlite3-api-worker1.js ceb1fc88d8a3742c069632e88fd05c14d5a79eb86bdb9e12969ec37f64fbf42b F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 -F ext/wasm/common/SqliteTestUtil.js e41a1406f18da9224523fad0c48885caf995b56956a5b9852909c0989e687e90 +F ext/wasm/common/SqliteTestUtil.js 04156a3b714b1b17a7261d21dd51341a8aeb9880a223e1e7519de98c2cceb414 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 F ext/wasm/common/whwasmutil.js 41b8e097e0a9cb07c24c0ede3c81b72470a63f4a4efb07f75586dc131569f5ae F ext/wasm/demo-oo1.html 75646855b38405d82781246fd08c852a2b3bee05dd9f0fe10ab655a8cffb79aa -F ext/wasm/demo-oo1.js 8be9c6be3c8e579eab4e7a5ee720ed122e38275a1f105169c6826193e42cf102 +F ext/wasm/demo-oo1.js 04e947b64a36ed8d6fe6d5e3ccee16ffc8b4461dd186e84f4baf44d53cc3aa72 F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 @@ -510,7 +510,7 @@ F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f74181 F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0 F ext/wasm/sqlite3-worker1.js e93fe8e5da7cb56dcf4d1bb0aa44bf681b509e1c56f2a75885c0f408f034c42b F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409 -F ext/wasm/testing1.js 9a97a7e45ce122b479b4a706ae1024abded67fd5f34a5764e41ff5efde8dfa17 +F ext/wasm/testing1.js 2def7a86c52ff28b145cb86188d5c7a49d5993f9b78c50d140e1c31551220955 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js e16ae385cd24c4a4ec5de6f1c02e621d243e1f179204ac8df31068faa9e31b1a F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x @@ -2006,8 +2006,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 9dbe9a6aecec43b51057375ef1d2d632db0d17eac8b7552c20cc91fc2f1a55d1 e5eaa80e81fdf86f2875a912b880272b8d099b82b08e945a7988c5dd0fe9d6b5 -R 98198d4c07340458e933c1601dce5a74 +P e215d55a97e1ccbca3101621374444d2381d87ef8e8fde5271e31c8b714e43e9 +R 46e3280ce6b3dabfd5a5df8623d5cf20 U stephan -Z cff3826d5874cd44103730340fc5de2d +Z e9aff16acc00367edea41a90ce619433 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 31636b0764..bb6efe14bd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e215d55a97e1ccbca3101621374444d2381d87ef8e8fde5271e31c8b714e43e9 \ No newline at end of file +ea2acc454c012a62556f6d0623d6eff60736d24aa214a64462b423623ef44d47 \ No newline at end of file From 8c45a9f952bed5b3d459bd04005b8afabc9c52a2 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 23 Aug 2022 17:02:46 +0000 Subject: [PATCH 022/428] Wasm build flag tweaks and documentation. FossilOrigin-Name: c8eb3aa8e0f487f14791214caf70d1aa03866e01345c7fa1d5607c24c39dde1d --- Makefile.in | 25 ++++++++++++++++--------- ext/wasm/GNUmakefile | 7 ++++++- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/Makefile.in b/Makefile.in index 56ab4c88a1..31cb05f4cc 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1543,15 +1543,20 @@ $(sqlite3_wasm_c): sqlite3.c #emcc_opt = -O3 emcc_opt = -Oz emcc_environment = web -# WASMFS OPFS is currently completely broken under fiddle but the -# root cause has not yet been determined. -emcc_flags_opfs = -#emcc_flags_opfs += -sWASMFS -pthread +# WASMFS/OPFS currently (2022-08-23) does not work with fiddle +# because (A) fiddle is primarily implemented as a Worker and (B) the +# Emscripten-based Worker loading process does not properly handle the +# case of nested Workers (necessary for it to load the WASMFS-specific +# Worker thread). +emcc_flags_wasmfs = +# To enable WASMFS/OPFS, uncomment these options: +#emcc_flags_wasmfs += -sWASMFS -pthread #emcc_environment = web,worker -#emcc_flags_opfs += -DSQLITE_WASM_OPFS -#emcc_flags_opfs += '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' -#emcc_flags_opfs += -sPTHREAD_POOL_SIZE=2 -#emcc_flags_opfs += -sPTHREAD_POOL_SIZE_STRICT=2 +#emcc_flags_wasmfs += -DSQLITE_WASM_OPFS +#emcc_flags_wasmfs += -sPTHREAD_POOL_SIZE=2 +#emcc_flags_wasmfs += -sPTHREAD_POOL_SIZE_STRICT=2 +# (Thread pool settings may require tweaking.) +#/end of WASMFS/OPFS options. emcc_flags = $(emcc_opt) \ -sALLOW_TABLE_GROWTH \ -sABORTING_MALLOC \ @@ -1563,10 +1568,12 @@ emcc_flags = $(emcc_opt) \ --minify 0 \ -I. $(SHELL_OPT) \ -DSQLITE_THREADSAFE=0 \ + -DSQLITE_TEMP_STORE=3 \ -DSQLITE_OMIT_UTF16 \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_OMIT_SHARED_CACHE \ - $(emcc_flags_opfs) + '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' \ + $(emcc_flags_wasmfs) $(fiddle_module_js): Makefile $(sqlite3_wasm_c) shell.c \ $(wasm_dir)/EXPORTED_RUNTIME_METHODS.fiddle \ $(wasm_dir)/EXPORTED_FUNCTIONS.fiddle diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 467034f9e8..db96a42ffc 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -18,6 +18,7 @@ else endif fiddle: $(MAKE) -C ../.. fiddle -e emcc_opt=$(fiddle_opt) +all: fiddle clean: $(MAKE) -C ../../ clean-fiddle @@ -46,8 +47,12 @@ SQLITE_OPT = \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_OMIT_UTF16 \ -DSQLITE_OMIT_SHARED_CACHE \ - -DSQLITE_THREADSAFE=0 + -DSQLITE_THREADSAFE=0 \ + -DSQLITE_TEMP_STORE=3 #SQLITE_OPT += -DSQLITE_ENABLE_MEMSYS5 +# ^^^ MEMSYS5 is hypothetically useful for non-Emscripten builds but +# requires adding more infrastructure and fixing one spot in the +# sqlite3 internals which calls malloc() early on. $(dir.top)/sqlite3.c: $(MAKE) -C $(dir.top) sqlite3.c diff --git a/manifest b/manifest index b297210960..ef10d9657a 100644 --- a/manifest +++ b/manifest @@ -1,9 +1,9 @@ -C More\sexperimentation\swith\show\sclient-side\sconfig\sstate\scan\sbe\spassed\son\sto\sinitialize\sthe\sJS-side\ssqlite3\sAPI. -D 2022-08-22T21:37:17.339 +C Wasm\sbuild\sflag\stweaks\sand\sdocumentation. +D 2022-08-23T17:02:46.959 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in 7d19258e83981c69dcdc63c46dfa83de907fc1c1c0472a6dc0d31b2fae376764 +F Makefile.in 544e93e4a996b7a1cd98ce8c2381cc487068a516e84243a6251fc3ba9638fe6c F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 F Makefile.msc d547a2fdba38a1c6cd1954977d0b0cc017f5f8fbfbc65287bf8d335808938016 F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 19c21ce4df5583278cc495bffbe03c69dc64af60fa9dac01766d0192bd191ac6 +F ext/wasm/GNUmakefile 79f0ddba6b6b474fad46a927aa9cd0fdbeb372c5332c84e8096d6b490df0f9db F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -2006,8 +2006,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e215d55a97e1ccbca3101621374444d2381d87ef8e8fde5271e31c8b714e43e9 -R 46e3280ce6b3dabfd5a5df8623d5cf20 +P ea2acc454c012a62556f6d0623d6eff60736d24aa214a64462b423623ef44d47 +R 72b1967923c17320161370eaaa7aa1c3 U stephan -Z e9aff16acc00367edea41a90ce619433 +Z 64cee0eefa2cd9236a38fc28a7dddd9f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bb6efe14bd..5d826619c0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ea2acc454c012a62556f6d0623d6eff60736d24aa214a64462b423623ef44d47 \ No newline at end of file +c8eb3aa8e0f487f14791214caf70d1aa03866e01345c7fa1d5607c24c39dde1d \ No newline at end of file From a9ac2ed069da67cfd540f5a35cbd15acabc9a00e Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 24 Aug 2022 00:10:45 +0000 Subject: [PATCH 023/428] Significant restructuring of the Worker #1 request/response object structures to improve readability and clarity. FossilOrigin-Name: 03b9db9b98cb36faa7de5a8a64d2e13c4aeaadfefb33ac92bb41056f6be3f121 --- ext/wasm/api/sqlite3-api-worker1.js | 143 +++++++++++++--------------- ext/wasm/sqlite3-worker1.js | 2 +- ext/wasm/testing2.js | 88 +++++++++-------- manifest | 16 ++-- manifest.uuid | 2 +- 5 files changed, 118 insertions(+), 133 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-worker1.js b/ext/wasm/api/sqlite3-api-worker1.js index a7b759851f..2e8d5a25d0 100644 --- a/ext/wasm/api/sqlite3-api-worker1.js +++ b/ext/wasm/api/sqlite3-api-worker1.js @@ -34,7 +34,7 @@ messages and then it posts a message in the form: ``` - {type:'sqlite3-api',data:'worker1-ready'} + {type:'sqlite3-api',result:'worker1-ready'} ``` to let the client know that it has been initialized. Clients may @@ -60,15 +60,6 @@ sqlite3.initWorker1API = function(){ - Support for handling multiple DBs via this interface is under development. - - - Revisit how virtual files are managed. We currently delete DBs - from the virtual filesystem when we close them, for the sake of - saving memory (the VFS lives in RAM). Supporting multiple DBs may - require that we give up that habit. Similarly, fully supporting - ATTACH, where a user can upload multiple DBs and ATTACH them, - also requires the that we manage the VFS entries better. - Related: we most definitely do not want to delete persistent DBs - (e.g. stored on OPFS) when they're closed. */ const toss = (...args)=>{throw new Error(args.join(' '))}; if('function' !== typeof importScripts){ @@ -102,10 +93,9 @@ sqlite3.initWorker1API = function(){ idSeq: 0, idMap: new WeakMap, open: function(arg){ - // TODO: if arg is a filename, look for a db in this.dbs with the + // TODO? if arg is a filename, look for a db in this.dbs with the // same filename and close/reopen it (or just pass it back as is?). if(!arg && this.defaultDb) return this.defaultDb; - //???if(this.defaultDb) this.defaultDb.close(); const db = (Array.isArray(arg) ? new DB(...arg) : new DB(arg)); this.dbs[getDbId(db)] = db; if(!this.defaultDb) this.defaultDb = db; @@ -122,12 +112,12 @@ sqlite3.initWorker1API = function(){ } } }, - post: function(type,data,xferList){ + post: function(msg,xferList){ if(xferList){ - self.postMessage( {type, data}, xferList ); + self.postMessage( msg, xferList ); xferList.length = 0; }else{ - self.postMessage({type, data}); + self.postMessage(msg); } }, /** Map of DB IDs to DBs. */ @@ -167,49 +157,42 @@ sqlite3.initWorker1API = function(){ xfer: [/*Temp holder for "transferable" postMessage() state.*/], /** Proxy for the DB constructor. Expects to be passed a single - object or a falsy value to use defaults. The object may - have a filename property to name the db file (see the DB - constructor for peculiarities and transformations) and/or a - buffer property (a Uint8Array holding a complete database - file's contents). The response is an object: + object or a falsy value to use defaults. The object may have a + filename property to name the db file (see the DB constructor + for peculiarities and transformations). The response is an + object: { filename: db filename (possibly differing from the input), - dbId: an opaque ID value which must be passed to other calls - in this API to tell them which db to use. If it is not - provided to future calls, they will default to - operating on the first-opened db. - - messageId: if the client-sent message included this field, - it is mirrored in the response. + dbId: an opaque ID value which must be passed in the message + envelope to other calls in this API to tell them which + db to use. If it is not provided to future calls, they + will default to operating on the first-opened db. } */ open: function(ev){ - const args = [], data = (ev.data || {}); - if(data.simulateError){ // undocumented internal testing option + const oargs = [], args = (ev.args || {}); + if(args.simulateError){ // undocumented internal testing option toss("Throwing because of simulateError flag."); } - if(data.filename) args.push(data.filename); - const db = wState.open(args); + if(args.filename) oargs.push(args.filename); + const db = wState.open(oargs); return { filename: db.filename, dbId: getDbId(db) }; }, /** - Proxy for DB.close(). If ev.data may either be a boolean or - an object with an `unlink` property. If that value is - truthy then the db file (if the db is currently open) will - be unlinked from the virtual filesystem, else it will be - kept intact. The response object is: + Proxy for DB.close(). ev.args may either be a boolean or an + object with an `unlink` property. If that value is truthy then + the db file (if the db is currently open) will be unlinked from + the virtual filesystem, else it will be kept intact. The + result object is: { filename: db filename _if_ the db is opened when this - is called, else the undefined value, - unlink: boolean. If true, unlink() (delete) the db file - after closing int. Any error while deleting it is - ignored. + is called, else the undefined value } It does not error if the given db is already closed or no db is @@ -222,8 +205,8 @@ sqlite3.initWorker1API = function(){ dbId: db ? getDbId(db) : undefined }; if(db){ - wState.close(db, !!((ev.data && 'object'===typeof ev.data) - ? ev.data.unlink : false)); + wState.close(db, ((ev.args && 'object'===typeof ev.args) + ? !!ev.args.unlink : false)); } return response; }, @@ -242,11 +225,11 @@ sqlite3.initWorker1API = function(){ message type key, in which case a callback function will be applied which posts each row result via: - postMessage({type: thatKeyType, data: theRow}) + postMessage({type: thatKeyType, row: theRow}) And, at the end of the result set (whether or not any result rows were produced), it will post an identical - message with data:null to alert the caller than the result + message with row:null to alert the caller than the result set is completed. The callback proxy must not recurse into this interface, or @@ -264,8 +247,8 @@ sqlite3.initWorker1API = function(){ */ exec: function(ev){ const opt = ( - 'string'===typeof ev.data - ) ? {sql: ev.data} : (ev.data || Object.create(null)); + 'string'===typeof ev.args + ) ? {sql: ev.args} : (ev.args || Object.create(null)); if(undefined===opt.rowMode){ /* Since the default rowMode of 'stmt' is not useful for the Worker interface, we'll default to @@ -286,13 +269,13 @@ sqlite3.initWorker1API = function(){ row as a message of that type. */ const that = this; opt.callback = - (row)=>wState.post(callbackMsgType,row,this.xfer); + (row)=>wState.post({type: callbackMsgType, row:row}, this.xfer); } try { db.exec(opt); if(opt.callback instanceof Function){ opt.callback = callbackMsgType; - wState.post(callbackMsgType, null); + wState.post({type: callbackMsgType, row: null}); } }/*catch(e){ console.warn("Worker is propagating:",e);throw e; @@ -305,7 +288,7 @@ sqlite3.initWorker1API = function(){ return opt; }/*exec()*/, /** - TO(re)DO, once we can abstract away access to the + TO(RE)DO, once we can abstract away access to the JS environment's virtual filesystem. Currently this always throws. @@ -352,73 +335,77 @@ sqlite3.initWorker1API = function(){ form: { type: apiCommand, - dbId: optional DB ID value (else uses a default db handle) - data: apiArguments, + dbId: optional DB ID value (else uses a default db handle), + args: apiArguments, messageId: optional client-specific value } As a rule, these commands respond with a postMessage() of their - own in the same form, but will, if needed, transform the `data` - member to an object and may add state to it. The responses - always have an object-format `data` part. If the inbound `data` - is an object which has a `messageId` property, that property is + own in the form: + + TODO: refactoring is underway. + + The responses always have an object-format `result` part. If the + inbound object has a `messageId` property, that property is always mirrored in the result object, for use in client-side dispatching of these asynchronous results. Exceptions thrown - during processing result in an `error`-type event with a - payload in the form: + during processing result in an `error`-type event with a payload + in the form: - { - message: error string, - errorClass: class name of the error type, + { type: 'error', dbId: DB handle ID, - input: ev.data, - [messageId: if set in the inbound message] + [messageId: if set in the inbound message], + result: { + message: error string, + errorClass: class name of the error type, + input: ev.data + } } The individual APIs are documented in the wMsgHandler object. */ self.onmessage = function(ev){ ev = ev.data; - let response, dbId = ev.dbId, evType = ev.type; + let result, dbId = ev.dbId, evType = ev.type; const arrivalTime = performance.now(); try { if(wMsgHandler.hasOwnProperty(evType) && wMsgHandler[evType] instanceof Function){ - response = wMsgHandler[evType](ev); + result = wMsgHandler[evType](ev); }else{ toss("Unknown db worker message type:",ev.type); } }catch(err){ evType = 'error'; - response = { + result = { message: err.message, errorClass: err.name, input: ev }; if(err.stack){ - response.stack = ('string'===typeof err.stack) + result.stack = ('string'===typeof err.stack) ? err.stack.split('\n') : err.stack; } if(0) console.warn("Worker is propagating an exception to main thread.", - "Reporting it _here_ for the stack trace:",err,response); - } - if(!response.messageId && ev.data - && 'object'===typeof ev.data && ev.data.messageId){ - response.messageId = ev.data.messageId; + "Reporting it _here_ for the stack trace:",err,result); } if(!dbId){ - dbId = response.dbId/*from 'open' cmd*/ + dbId = result.dbId/*from 'open' cmd*/ || getDefaultDbId(); } - if(!response.dbId) response.dbId = dbId; // Timing info is primarily for use in testing this API. It's not part of // the public API. arrivalTime = when the worker got the message. - response.workerReceivedTime = arrivalTime; - response.workerRespondTime = performance.now(); - response.departureTime = ev.departureTime; - wState.post(evType, response, wMsgHandler.xfer); + wState.post({ + type: evType, + dbId: dbId, + messageId: ev.messageId, + workerReceivedTime: arrivalTime, + workerRespondTime: performance.now(), + departureTime: ev.departureTime, + result: result + }, wMsgHandler.xfer); }; - setTimeout(()=>self.postMessage({type:'sqlite3-api',data:'worker1-ready'}), 0); + setTimeout(()=>self.postMessage({type:'sqlite3-api',result:'worker1-ready'}), 0); }.bind({self, sqlite3}); }); diff --git a/ext/wasm/sqlite3-worker1.js b/ext/wasm/sqlite3-worker1.js index 3982ddaa2b..ff024d8215 100644 --- a/ext/wasm/sqlite3-worker1.js +++ b/ext/wasm/sqlite3-worker1.js @@ -14,7 +14,7 @@ sqlite3.js, initializes the module, and postMessage()'s a message after the module is initialized: - {type: 'sqlite3-api', data: 'worker1-ready'} + {type: 'sqlite3-api', result: 'worker1-ready'} This seemingly superfluous level of indirection is necessary when loading sqlite3.js via a Worker. Instantiating a worker with new diff --git a/ext/wasm/testing2.js b/ext/wasm/testing2.js index bcbe7b50de..b051cc04cc 100644 --- a/ext/wasm/testing2.js +++ b/ext/wasm/testing2.js @@ -31,17 +31,6 @@ const warn = console.warn.bind(console); const error = console.error.bind(console); const toss = (...args)=>{throw new Error(args.join(' '))}; - /** Posts a worker message as {type:type, data:data}. */ - const wMsg = function(type,data){ - log("Posting message to worker dbId="+(DbState.id||'default')+':',data); - SW.postMessage({ - type, - dbId: DbState.id, - data, - departureTime: performance.now() - }); - return SW; - }; SW.onerror = function(event){ error("onerror",event); @@ -74,28 +63,37 @@ logHtml("","Total test count:",T.counter+". Total time =",(performance.now() - startTime),"ms"); }; - const logEventResult = function(evd){ + const logEventResult = function(ev){ + const evd = ev.result; logHtml(evd.errorClass ? 'error' : '', - "runOneTest",evd.messageId,"Worker time =", - (evd.workerRespondTime - evd.workerReceivedTime),"ms.", + "runOneTest",ev.messageId,"Worker time =", + (ev.workerRespondTime - ev.workerReceivedTime),"ms.", "Round-trip event time =", - (performance.now() - evd.departureTime),"ms.", - (evd.errorClass ? evd.message : "") + (performance.now() - ev.departureTime),"ms.", + (evd.errorClass ? ev.message : ""), evd ); }; - const runOneTest = function(eventType, eventData, callback){ - T.assert(eventData && 'object'===typeof eventData); + const runOneTest = function(eventType, eventArgs, callback){ + T.assert(eventArgs && 'object'===typeof eventArgs); /* ^^^ that is for the testing and messageId-related code, not a hard requirement of all of the Worker-exposed APIs. */ - eventData.messageId = MsgHandlerQueue.push(eventType,function(ev){ - logEventResult(ev.data); + const messageId = MsgHandlerQueue.push(eventType,function(ev){ + logEventResult(ev); if(callback instanceof Function){ callback(ev); testCount(); } }); - wMsg(eventType, eventData); + const msg = { + type: eventType, + args: eventArgs, + dbId: DbState.id, + messageId: messageId, + departureTime: performance.now() + }; + log("Posting",eventType,"message to worker dbId="+(DbState.id||'default')+':',msg); + SW.postMessage(msg); }; /** Methods which map directly to onmessage() event.type keys. @@ -103,23 +101,23 @@ const dbMsgHandler = { open: function(ev){ DbState.id = ev.dbId; - log("open result",ev.data); + log("open result",ev); }, exec: function(ev){ - log("exec result",ev.data); + log("exec result",ev); }, export: function(ev){ - log("export result",ev.data); + log("export result",ev); }, error: function(ev){ - error("ERROR from the worker:",ev.data); - logEventResult(ev.data); + error("ERROR from the worker:",ev); + logEventResult(ev); }, resultRowTest1: function f(ev){ if(undefined === f.counter) f.counter = 0; - if(ev.data) ++f.counter; - //log("exec() result row:",ev.data); - T.assert(null===ev.data || 'number' === typeof ev.data.b); + if(ev.row) ++f.counter; + //log("exec() result row:",ev.row); + T.assert(null===ev.row || 'number' === typeof ev.row.b); } }; @@ -149,7 +147,7 @@ multi: true, resultRows: [], columnNames: [] }, function(ev){ - ev = ev.data; + ev = ev.result; T.assert(0===ev.resultRows.length) .assert(0===ev.columnNames.length); }); @@ -157,7 +155,7 @@ sql: 'select a a, b b from t order by a', resultRows: [], columnNames: [], }, function(ev){ - ev = ev.data; + ev = ev.result; T.assert(3===ev.resultRows.length) .assert(1===ev.resultRows[0][0]) .assert(6===ev.resultRows[2][1]) @@ -169,7 +167,7 @@ resultRows: [], columnNames: [], rowMode: 'object' }, function(ev){ - ev = ev.data; + ev = ev.result; T.assert(3===ev.resultRows.length) .assert(1===ev.resultRows[0].a) .assert(6===ev.resultRows[2].b) @@ -181,7 +179,7 @@ resultRows: [], //rowMode: 'array', // array is the default in the Worker interface }, function(ev){ - ev = ev.data; + ev = ev.result; T.assert(1 === ev.resultRows.length) .assert(1 === ev.resultRows[0][0]); }); @@ -206,7 +204,7 @@ rowMode: 1, resultRows: [] },function(ev){ - const rows = ev.data.resultRows; + const rows = ev.result.resultRows; T.assert(3===rows.length). assert(6===rows[0]); }); @@ -215,14 +213,14 @@ sql: 'select count(a) from t', resultRows: [] },function(ev){ - ev = ev.data; + ev = ev.result; T.assert(1===ev.resultRows.length) .assert(2===ev.resultRows[0][0]); }); if(0){ // export requires reimpl. for portability reasons. runOneTest('export',{}, function(ev){ - ev = ev.data; + ev = ev.result; T.assert('string' === typeof ev.filename) .assert(ev.buffer instanceof Uint8Array) .assert(ev.buffer.length > 1024) @@ -231,11 +229,11 @@ } /***** close() tests must come last. *****/ runOneTest('close',{unlink:true},function(ev){ - ev = ev.data; + ev = ev.result; T.assert('string' === typeof ev.filename); }); runOneTest('close',{unlink:true},function(ev){ - ev = ev.data; + ev = ev.result; T.assert(undefined === ev.filename); }); }; @@ -276,11 +274,11 @@ filename:'testing2.sqlite3', simulateError: simulateOpenError }, function(ev){ - //log("open result",ev); - T.assert('testing2.sqlite3'===ev.data.filename) - .assert(ev.data.dbId) - .assert(ev.data.messageId); - DbState.id = ev.data.dbId; + log("open result",ev); + T.assert('testing2.sqlite3'===ev.result.filename) + .assert(ev.dbId) + .assert(ev.messageId); + DbState.id = ev.dbId; if(waitForOpen) setTimeout(runTests2, 0); }); if(!waitForOpen) runTests2(); @@ -293,7 +291,7 @@ } ev = ev.data/*expecting a nested object*/; //log("main window onmessage:",ev); - if(ev.data && ev.data.messageId){ + if(ev.result && ev.messageId){ /* We're expecting a queued-up callback handler. */ const f = MsgHandlerQueue.shift(); if('error'===ev.type){ @@ -306,7 +304,7 @@ } switch(ev.type){ case 'sqlite3-api': - switch(ev.data){ + switch(ev.result){ case 'worker1-ready': log("Message:",ev); self.sqlite3TestModule.setStatus(null); diff --git a/manifest b/manifest index ef10d9657a..fc1e6d40bd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Wasm\sbuild\sflag\stweaks\sand\sdocumentation. -D 2022-08-23T17:02:46.959 +C Significant\srestructuring\sof\sthe\sWorker\s#1\srequest/response\sobject\sstructures\sto\simprove\sreadability\sand\sclarity. +D 2022-08-24T00:10:45.121 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -486,7 +486,7 @@ F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed F ext/wasm/api/sqlite3-api-oo1.js f6dcaac3270182471f97efcfda25bd4a4ac1777b8ec52ebd1c6846721160e54c F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 F ext/wasm/api/sqlite3-api-prologue.js 6e0e7787ed955ea2b6158e0bb7608f63b54236847700d183e49e1f10d0525b8f -F ext/wasm/api/sqlite3-api-worker1.js ceb1fc88d8a3742c069632e88fd05c14d5a79eb86bdb9e12969ec37f64fbf42b +F ext/wasm/api/sqlite3-api-worker1.js c9e4edb89f41a4fa65d136ae180c1bc0beb694eb95f7d9e6936fbb702914c160 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 F ext/wasm/common/SqliteTestUtil.js 04156a3b714b1b17a7261d21dd51341a8aeb9880a223e1e7519de98c2cceb414 @@ -508,11 +508,11 @@ F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb43 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0 -F ext/wasm/sqlite3-worker1.js e93fe8e5da7cb56dcf4d1bb0aa44bf681b509e1c56f2a75885c0f408f034c42b +F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409 F ext/wasm/testing1.js 2def7a86c52ff28b145cb86188d5c7a49d5993f9b78c50d140e1c31551220955 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 -F ext/wasm/testing2.js e16ae385cd24c4a4ec5de6f1c02e621d243e1f179204ac8df31068faa9e31b1a +F ext/wasm/testing2.js f01e8d7b32f6d4790f8352bf8dc17d2b860afa58f6c8ea1f824ef1c0d2068138 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2006,8 +2006,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P ea2acc454c012a62556f6d0623d6eff60736d24aa214a64462b423623ef44d47 -R 72b1967923c17320161370eaaa7aa1c3 +P c8eb3aa8e0f487f14791214caf70d1aa03866e01345c7fa1d5607c24c39dde1d +R ea27eb9ac1a6ed815943946a889ca949 U stephan -Z 64cee0eefa2cd9236a38fc28a7dddd9f +Z 3af622548a101a1182c5d00aa6a9dd08 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5d826619c0..9b2d03d93c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c8eb3aa8e0f487f14791214caf70d1aa03866e01345c7fa1d5607c24c39dde1d \ No newline at end of file +03b9db9b98cb36faa7de5a8a64d2e13c4aeaadfefb33ac92bb41056f6be3f121 \ No newline at end of file From efeee19a958b905cc8e939e54b2959089bb89108 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 24 Aug 2022 00:51:39 +0000 Subject: [PATCH 024/428] The very basics of a Promise-based proxy for the Worker #1 API. Still requires considerable cleanup, testing, and a solution for the exec-callback-via-event-type-name problem. FossilOrigin-Name: 1e447849fb65887e806e3348a8a68f70ea6802bc0a1e56c385a279f27cc0cdda --- ext/wasm/common/SqliteTestUtil.js | 5 +- ext/wasm/testing-worker-promise.html | 33 +++++++ ext/wasm/testing-worker-promise.js | 139 +++++++++++++++++++++++++++ manifest | 14 +-- manifest.uuid | 2 +- 5 files changed, 182 insertions(+), 11 deletions(-) create mode 100644 ext/wasm/testing-worker-promise.html create mode 100644 ext/wasm/testing-worker-promise.js diff --git a/ext/wasm/common/SqliteTestUtil.js b/ext/wasm/common/SqliteTestUtil.js index 4b4cefbf0c..c21469c9b0 100644 --- a/ext/wasm/common/SqliteTestUtil.js +++ b/ext/wasm/common/SqliteTestUtil.js @@ -191,10 +191,7 @@ */ initSqlite3: function(){ self.sqlite3ApiConfig = this.sqlite3ApiConfig; - return self.sqlite3InitModule(this).then(function(M){ - delete self.sqlite3ApiConfig; - return M; - }); + return self.sqlite3InitModule(this).finally(()=>delete self.sqlite3ApiConfig); } }; })(self/*window or worker*/); diff --git a/ext/wasm/testing-worker-promise.html b/ext/wasm/testing-worker-promise.html new file mode 100644 index 0000000000..eab30c301f --- /dev/null +++ b/ext/wasm/testing-worker-promise.html @@ -0,0 +1,33 @@ + + + + + + + + + worker-promise tests + + +
worker-promise tests
+ +
+
+
Initializing app...
+
+ 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. +
+
+
Downloading...
+
+ +
+
Most stuff on this page happens in the dev console.
+
+
+ + + + diff --git a/ext/wasm/testing-worker-promise.js b/ext/wasm/testing-worker-promise.js new file mode 100644 index 0000000000..c60c6c2882 --- /dev/null +++ b/ext/wasm/testing-worker-promise.js @@ -0,0 +1,139 @@ +/* + 2022-08-23 + + The author disclaims copyright to this source code. In place of a + legal notice, here is a blessing: + + * May you do good and not evil. + * May you find forgiveness for yourself and forgive others. + * May you share freely, never taking more than you give. + + *********************************************************************** + + + UNDER CONSTRUCTION: a Promise-based proxy for for the sqlite3 Worker + #1 API. +*/ +'use strict'; +(function(){ + const T = self.SqliteTestUtil; + const DbState = { + id: undefined + }; + const eOutput = document.querySelector('#test-output'); + const log = console.log.bind(console); + const logHtml = async function(cssClass,...args){ + log.apply(this, args); + const ln = document.createElement('div'); + if(cssClass) ln.classList.add(cssClass); + ln.append(document.createTextNode(args.join(' '))); + eOutput.append(ln); + }; + const warn = console.warn.bind(console); + const error = console.error.bind(console); + + let startTime; + const logEventResult = async function(evd){ + logHtml(evd.errorClass ? 'error' : '', + "response to",evd.messageId,"Worker time =", + (evd.workerRespondTime - evd.workerReceivedTime),"ms.", + "Round-trip event time =", + (performance.now() - evd.departureTime),"ms.", + (evd.errorClass ? evd.message : "") + ); + }; + + const testCount = async ()=>{ + logHtml("","Total test count:",T.counter+". Total time =",(performance.now() - startTime),"ms"); + }; + + // Inspiration: https://stackoverflow.com/a/52439530 + const worker = new Worker("sqlite3-worker1.js"); + worker.onerror = function(event){ + error("worker.onerror",event); + }; + const WorkerPromiseHandler = Object.create(null); + WorkerPromiseHandler.nextId = function f(){ + return 'msg#'+(f._ = (f._ || 0) + 1); + }; + + /** Posts a worker message as {type:eventType, data:eventData}. */ + const requestWork = async function(eventType, eventData){ + //log("requestWork", eventType, eventData); + T.assert(eventData && 'object'===typeof eventData); + /* ^^^ that is for the testing and messageId-related code, not + a hard requirement of all of the Worker-exposed APIs. */ + const wph = WorkerPromiseHandler; + const msgId = wph.nextId(); + const proxy = wph[msgId] = Object.create(null); + proxy.promise = new Promise(function(resolve, reject){ + proxy.resolve = resolve; + proxy.reject = reject; + const msg = { + type: eventType, + args: eventData, + dbId: DbState.id, + messageId: msgId, + departureTime: performance.now() + }; + log("Posting",eventType,"message to worker dbId="+(DbState.id||'default')+':',msg); + worker.postMessage(msg); + }); + log("Set up promise",proxy); + return proxy.promise; + }; + + + const runOneTest = async function(eventType, eventData, callback){ + T.assert(eventData && 'object'===typeof eventData); + /* ^^^ that is for the testing and messageId-related code, not + a hard requirement of all of the Worker-exposed APIs. */ + let p = requestWork(eventType, eventData); + if(callback) p.then(callback).finally(testCount); + return p; + }; + + const runTests = async function(){ + logHtml('', + "Sending 'open' message and waiting for its response before continuing."); + startTime = performance.now(); + runOneTest('open', { + filename:'testing2.sqlite3', + simulateError: 0 /* if true, fail the 'open' */ + }, function(ev){ + log("then open result",ev); + T.assert('testing2.sqlite3'===ev.result.filename) + .assert(ev.dbId) + .assert(ev.messageId) + .assert(DbState.id === ev.dbId); + }).catch((err)=>error("error response:",err)); + }; + + worker.onmessage = function(ev){ + ev = ev.data; + (('error'===ev.type) ? error : log)('worker.onmessage',ev); + const msgHandler = WorkerPromiseHandler[ev.messageId]; + if(!msgHandler){ + if('worker1-ready'===ev.result) { + /*sqlite3-api/worker1-ready is fired when the Worker1 API initializes*/ + self.sqlite3TestModule.setStatus(null)/*hide the HTML-side is-loading spinner*/; + runTests(); + return; + } + error("Unhandled worker message:",ev); + return; + } + logEventResult(ev); + delete WorkerPromiseHandler[ev.messageId]; + if('error'===ev.type){ + msgHandler.reject(ev); + } + else{ + if(!DbState.id && ev.dbId) DbState.id = ev.dbId; + msgHandler.resolve(ev); // async, so testCount() results on next line are out of order + //testCount(); + } + }; + + log("Init complete, but async init bits may still be running."); +})(); diff --git a/manifest b/manifest index fc1e6d40bd..d26d33dea6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Significant\srestructuring\sof\sthe\sWorker\s#1\srequest/response\sobject\sstructures\sto\simprove\sreadability\sand\sclarity. -D 2022-08-24T00:10:45.121 +C The\svery\sbasics\sof\sa\sPromise-based\sproxy\sfor\sthe\sWorker\s#1\sAPI.\sStill\srequires\sconsiderable\scleanup,\stesting,\sand\sa\ssolution\sfor\sthe\sexec-callback-via-event-type-name\sproblem. +D 2022-08-24T00:51:39.887 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -489,7 +489,7 @@ F ext/wasm/api/sqlite3-api-prologue.js 6e0e7787ed955ea2b6158e0bb7608f63b54236847 F ext/wasm/api/sqlite3-api-worker1.js c9e4edb89f41a4fa65d136ae180c1bc0beb694eb95f7d9e6936fbb702914c160 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 -F ext/wasm/common/SqliteTestUtil.js 04156a3b714b1b17a7261d21dd51341a8aeb9880a223e1e7519de98c2cceb414 +F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 F ext/wasm/common/whwasmutil.js 41b8e097e0a9cb07c24c0ede3c81b72470a63f4a4efb07f75586dc131569f5ae @@ -509,6 +509,8 @@ F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e +F ext/wasm/testing-worker-promise.html ba3d5423cfbdc96c332af3632dfcb61527ba8fd7e487b3bf3f07542f890c3e08 +F ext/wasm/testing-worker-promise.js c05c46a3a22b1910f6a1db11f3da6df701259eaa1277ddba085247b7f9059423 F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409 F ext/wasm/testing1.js 2def7a86c52ff28b145cb86188d5c7a49d5993f9b78c50d140e1c31551220955 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 @@ -2006,8 +2008,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P c8eb3aa8e0f487f14791214caf70d1aa03866e01345c7fa1d5607c24c39dde1d -R ea27eb9ac1a6ed815943946a889ca949 +P 03b9db9b98cb36faa7de5a8a64d2e13c4aeaadfefb33ac92bb41056f6be3f121 +R 5b16a785d8c414a8eb9d618c8d9d8cea U stephan -Z 3af622548a101a1182c5d00aa6a9dd08 +Z fdaa5cf9f1bfd4215c6ebf07223819c1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9b2d03d93c..1b823c2233 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -03b9db9b98cb36faa7de5a8a64d2e13c4aeaadfefb33ac92bb41056f6be3f121 \ No newline at end of file +1e447849fb65887e806e3348a8a68f70ea6802bc0a1e56c385a279f27cc0cdda \ No newline at end of file From 9a34509a06ad893ae3ac786363ebf8d29b3e3a7c Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 24 Aug 2022 05:59:23 +0000 Subject: [PATCH 025/428] More work on how to configure the sqlite3 JS API bootstrapping process from higher-level code. Initial version of sqlite3-worker1-promiser, a Promise-based proxy for the Worker API #1. FossilOrigin-Name: b030f321bd5a38cdd5d6f6735f201afa62d30d2b0ba02e67f055b4895553a878 --- ext/wasm/api/sqlite3-api-cleanup.js | 45 ++-- ext/wasm/api/sqlite3-api-prologue.js | 48 +++- ext/wasm/api/sqlite3-api-worker1.js | 37 ++- ext/wasm/sqlite3-worker1-promiser.js | 232 ++++++++++++++++++ ext/wasm/testing-worker-promise.js | 139 ----------- ...ise.html => testing-worker1-promiser.html} | 3 +- ext/wasm/testing-worker1-promiser.js | 214 ++++++++++++++++ manifest | 21 +- manifest.uuid | 2 +- 9 files changed, 537 insertions(+), 204 deletions(-) create mode 100644 ext/wasm/sqlite3-worker1-promiser.js delete mode 100644 ext/wasm/testing-worker-promise.js rename ext/wasm/{testing-worker-promise.html => testing-worker1-promiser.html} (91%) create mode 100644 ext/wasm/testing-worker1-promiser.js diff --git a/ext/wasm/api/sqlite3-api-cleanup.js b/ext/wasm/api/sqlite3-api-cleanup.js index d989faccaf..ed6b8c40ea 100644 --- a/ext/wasm/api/sqlite3-api-cleanup.js +++ b/ext/wasm/api/sqlite3-api-cleanup.js @@ -18,44 +18,24 @@ 'use strict'; if('undefined' !== typeof Module){ // presumably an Emscripten build /** - Replace sqlite3ApiBootstrap() with a variant which plugs in the - Emscripten-based config for all config options which the client - does not provide. + Install a suitable default configuration for sqlite3ApiBootstrap(). */ - const SAB = self.sqlite3ApiBootstrap; - self.sqlite3ApiBootstrap = function(apiConfig){ - apiConfig = apiConfig || {}; - const configDefaults = { - Module: Module /* ==> Emscripten-style Module object. Currently - needs to be exposed here for test code. NOT part - of the public API. */, - exports: Module['asm'], - memory: Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */ - }; - const config = {}; - Object.keys(configDefaults).forEach(function(k){ - config[k] = Object.getOwnPropertyDescriptor(apiConfig, k) - ? apiConfig[k] : configDefaults[k]; - }); - // Copy over any properties apiConfig defines but configDefaults does not... - Object.keys(apiConfig).forEach(function(k){ - if(!Object.getOwnPropertyDescriptor(config, k)){ - config[k] = apiConfig[k]; - } - }); - return SAB(config); - }; + const SABC = self.sqlite3ApiBootstrap.defaultConfig; + SABC.Module = Module /* ==> Current needs to be exposed here for test code. NOT part + of the public API. */; + SABC.exports = Module['asm']; + SABC.memory = Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */; /** For current (2022-08-22) purposes, automatically call sqlite3ApiBootstrap(). That decision will be revisited at some point, as we really want client code to be able to call this to - configure certain parts. If the global sqliteApiConfig property - is available, it is assumed to be a config object for - sqlite3ApiBootstrap(). + configure certain parts. Clients may modify + self.sqlite3ApiBootstrap.defaultConfig to tweak the default + configuration used by a no-args call to sqlite3ApiBootstrap(). */ //console.warn("self.sqlite3ApiConfig = ",self.sqlite3ApiConfig); - const sqlite3 = self.sqlite3ApiBootstrap(self.sqlite3ApiConfig || Object.create(null)); + const sqlite3 = self.sqlite3ApiBootstrap(); delete self.sqlite3ApiBootstrap; if(self.location && +self.location.port > 1024){ @@ -67,4 +47,9 @@ if('undefined' !== typeof Module){ // presumably an Emscripten build delete sqlite3.capi.util /* arguable, but these are (currently) internal-use APIs */; //console.warn("Module.sqlite3 =",Module.sqlite3); Module.sqlite3 = sqlite3 /* Currently needed by test code and sqlite3-worker1.js */; +}else{ + console.warn("This is not running in an Emscripten module context, so", + "self.sqlite3ApiBootstrap() is _not_ being called due to lack", + "of config info for the WASM environment.", + "It must be called manually."); } diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 1a38f0343a..d92b948fce 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -93,16 +93,16 @@ The config object properties include: - - `Module`: Emscripten-style module object. Currently only required + - `Module`[^1]: Emscripten-style module object. Currently only required by certain test code and is _not_ part of the public interface. (TODO: rename this to EmscriptenModule to be more explicit.) - - `exports`: the "exports" object for the current WASM + - `exports`[^1]: the "exports" object for the current WASM environment. In an Emscripten build, this should be set to `Module['asm']`. - - `memory`: optional WebAssembly.Memory object, defaulting to - `exports.memory`. In Emscripten environments this should be set + - `memory`[^1]: optional WebAssembly.Memory object, defaulting to + `exports.memory`. In Emscripten environments this should be set to `Module.wasmMemory` if the build uses `-sIMPORT_MEMORY`, or be left undefined/falsy to default to `exports.memory` when using WASM-exported memory. @@ -120,20 +120,26 @@ the `free(3)`-compatible routine for the WASM environment. Defaults to `"free"`. - - `persistentDirName`: if the environment supports persistent storage, this + - `persistentDirName`[^1]: if the environment supports persistent storage, this directory names the "mount point" for that directory. It must be prefixed by `/` and may currently contain only a single directory-name part. Using the root directory name is not supported by any current persistent backend. + + + [^1] = This property may optionally be a function, in which case this + function re-assigns it to the value returned from that function, + enabling delayed evaluation. + */ -self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(apiConfig){ - 'use strict'; - +'use strict'; +self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( + apiConfig = (sqlite3ApiBootstrap.defaultConfig || self.sqlite3ApiConfig) +){ if(sqlite3ApiBootstrap.sqlite3){ /* already initalized */ console.warn("sqlite3ApiBootstrap() called multiple times.", "Config and external initializers are ignored on calls after the first."); return sqlite3ApiBootstrap.sqlite3; } - apiConfig = apiConfig || {}; const config = Object.create(null); { @@ -158,6 +164,16 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(apiConfig){ }); } + [ + // If any of these config options are functions, replace them with + // the result of calling that function... + 'Module', 'exports', 'memory', 'persistentDirName' + ].forEach((k)=>{ + if('function' === typeof config[k]){ + config[k] = config[k](); + } + }); + /** Throws a new Error, the message of which is the concatenation all args with a space between each. */ const toss = (...args)=>{throw new Error(args.join(' '))}; @@ -750,4 +766,16 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(apiConfig){ this array is deleted. */ self.sqlite3ApiBootstrap.initializers = []; -self.sqlite3ApiBootstrap.sqlite3 = undefined /* installed at first call */; +/** + Client code may assign sqlite3ApiBootstrap.defaultConfig an + object-type value before calling sqlite3ApiBootstrap() (without + arguments) in order to tell that call to use this object as its + default config value. The intention of this is to provide + downstream clients with a reasonably flexible approach for plugging in + an environment-suitable configuration without having to define a new + global-scope symbol. +*/ +self.sqlite3ApiBootstrap.defaultConfig = Object.create(null); +/** Placeholder: gets installed by the first call to + self.sqlite3ApiBootstrap(). */ +self.sqlite3ApiBootstrap.sqlite3 = undefined; diff --git a/ext/wasm/api/sqlite3-api-worker1.js b/ext/wasm/api/sqlite3-api-worker1.js index 2e8d5a25d0..39263a4abe 100644 --- a/ext/wasm/api/sqlite3-api-worker1.js +++ b/ext/wasm/api/sqlite3-api-worker1.js @@ -92,11 +92,8 @@ sqlite3.initWorker1API = function(){ defaultDb: undefined, idSeq: 0, idMap: new WeakMap, - open: function(arg){ - // TODO? if arg is a filename, look for a db in this.dbs with the - // same filename and close/reopen it (or just pass it back as is?). - if(!arg && this.defaultDb) return this.defaultDb; - const db = (Array.isArray(arg) ? new DB(...arg) : new DB(arg)); + open: function(opt){ + const db = new DB(opt.filename); this.dbs[getDbId(db)] = db; if(!this.defaultDb) this.defaultDb = db; return db; @@ -169,14 +166,26 @@ sqlite3.initWorker1API = function(){ envelope to other calls in this API to tell them which db to use. If it is not provided to future calls, they will default to operating on the first-opened db. + + persistent: prepend sqlite3.capi.sqlite3_web_persistent_dir() + to the given filename so that it is stored + in persistent storage _if_ the environment supports it. + If persistent storage is not supported, the filename + is used as-is. } */ open: function(ev){ - const oargs = [], args = (ev.args || {}); + const oargs = Object.create(null), args = (ev.args || Object.create(null)); if(args.simulateError){ // undocumented internal testing option toss("Throwing because of simulateError flag."); } - if(args.filename) oargs.push(args.filename); + if(args.persistent && args.filename){ + oargs.filaname = sqlite3.capi.sqlite3_web_persistent_dir() + args.filename; + }else if('' === args.filename){ + oargs.filename = args.filename; + }else{ + oargs.filename = args.filename || ':memory:'; + } const db = wState.open(oargs); return { filename: db.filename, @@ -184,15 +193,15 @@ sqlite3.initWorker1API = function(){ }; }, /** - Proxy for DB.close(). ev.args may either be a boolean or an - object with an `unlink` property. If that value is truthy then - the db file (if the db is currently open) will be unlinked from - the virtual filesystem, else it will be kept intact. The - result object is: + Proxy for DB.close(). ev.args may be elided or an object with + an `unlink` property. If that value is truthy then the db file + (if the db is currently open) will be unlinked from the virtual + filesystem, else it will be kept intact. The result object is: { filename: db filename _if_ the db is opened when this is called, else the undefined value + dbId: the ID of the closed b, or undefined if none is closed } It does not error if the given db is already closed or no db is @@ -356,6 +365,7 @@ sqlite3.initWorker1API = function(){ dbId: DB handle ID, [messageId: if set in the inbound message], result: { + operation: "inbound message's 'type' value", message: error string, errorClass: class name of the error type, input: ev.data @@ -378,6 +388,7 @@ sqlite3.initWorker1API = function(){ }catch(err){ evType = 'error'; result = { + operation: ev.type, message: err.message, errorClass: err.name, input: ev @@ -405,7 +416,7 @@ sqlite3.initWorker1API = function(){ result: result }, wMsgHandler.xfer); }; - setTimeout(()=>self.postMessage({type:'sqlite3-api',result:'worker1-ready'}), 0); + self.postMessage({type:'sqlite3-api',result:'worker1-ready'}); }.bind({self, sqlite3}); }); diff --git a/ext/wasm/sqlite3-worker1-promiser.js b/ext/wasm/sqlite3-worker1-promiser.js new file mode 100644 index 0000000000..c01ed9a5c6 --- /dev/null +++ b/ext/wasm/sqlite3-worker1-promiser.js @@ -0,0 +1,232 @@ +/* + 2022-08-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. + + *********************************************************************** + + This file implements a Promise-based proxy for the sqlite3 Worker + API #1. It is intended to be included either from the main thread or + a Worker, but only if (A) the environment supports nested Workers + and (B) it's _not_ a Worker which loads the sqlite3 WASM/JS + module. This file's features will load that module and provide a + slightly simpler client-side interface than the slightly-lower-level + Worker API does. + + This script necessarily exposes on global symbol, but clients may + freely `delete` that symbol after calling it. +*/ +'use strict'; +/** + Configures an sqlite3 Worker API #1 Worker such that it can be + manipulated via a Promise-based interface and returns a factory + function which returns Promises for communicating with the worker. + This proxy has an _almost_ identical interface to the normal + worker API, with any exceptions noted below. + + It requires a configuration object with the following properties: + + - `worker` (required): a Worker instance which loads + `sqlite3-worker1.js` or a functional equivalent. Note that this + function replaces the worker.onmessage property. This property + may alternately be a function, in which case this function + re-assigns this property with the result of calling that + function, enabling delayed instantiation of a Worker. + + - `onready` (optional, but...): this callback is called with no + arguments when the worker fires its initial + 'sqlite3-api'/'worker1-ready' message, which it does when + sqlite3.initWorker1API() completes its initialization. This is + the simplest way to tell the worker to kick of work at the + earliest opportunity. + + - `onerror` (optional): a callback to pass error-type events from + the worker. The object passed to it will be the error message + payload from the worker. This is _not_ the same as the + worker.onerror property! + + - `onunhandled` (optional): a callback which gets passed the + message event object for any worker.onmessage() events which + are not handled by this proxy. Ideally that "should" never + happen, as this proxy aims to handle all known message types. + + - `generateMessageId` (optional): a function which, when passed + an about-to-be-posted message object, generates a _unique_ + message ID for the message, which this API then assigns as the + messageId property of the message. It _must_ generate unique + IDs so that dispatching can work. If not defined, a default + generator is used. + + - `dbId` (optional): is the database ID to be used by the + worker. This must initially be unset or a falsy value. The + first `open` message sent to the worker will cause this config + entry to be assigned to the ID of the opened database. That ID + "should" be set as the `dbId` property of the message sent in + future requests, so that the worker uses that database. + However, if the worker is not given an explicit dbId, it will + use the first-opened database by default. If client code needs + to work with multiple database IDs, the client-level code will + need to juggle those themselves. A `close` message will clear + this property if it matches the ID of the closed db. Potential + TODO: add a config callback specifically for reporting `open` + and `close` message results, so that clients may track those + values. + + - `debug` (optional): a console.debug()-style function for logging + information about messages. + + + This function returns a stateful factory function with the following + interfaces: + + - Promise function(messageType, messageArgs) + - Promise function({message object}) + + The first form expects the "type" and "args" values for a Worker + message. The second expects an object in the form {type:..., + args:...} plus any other properties the client cares to set. This + function will always set the messageId property on the object, + even if it's already set, and will set the dbId property to + config.dbId if it is _not_ set in the message object. + + The function throws on error. + + The function installs a temporarily message listener, posts a + message to the configured Worker, and handles the message's + response via the temporary message listener. The then() callback + of the returned Promise is passed the `message.data` property from + the resulting message, i.e. the payload from the worker, stripped + of the lower-level event state which the onmessage() handler + receives. + + Example usage: + + ``` + const config = {...}; + const eventPromiser = sqlite3Worker1Promiser(config); + eventPromiser('open', {filename:"/foo.db"}).then(function(msg){ + console.log("open response",msg); // => {type:'open', result: {filename:'/foo.db'}, ...} + // Recall that config.dbId will be set for the first 'open' + // call and cleared for a matching 'close' call. + }); + eventPromiser({type:'close'}).then((msg)=>{ + console.log("open response",msg); // => {type:'open', result: {filename:'/foo.db'}, ...} + // Recall that config.dbId will be used by default for the message's dbId if + // none is explicitly provided, and a 'close' op will clear config.dbId if it + // closes that exact db. + }); + ``` + + Differences from Worker API #1: + + - exec's {callback: STRING} option does not work via this + interface (it triggers an exception), but {callback: function} + does and works exactly like the STRING form does in the Worker: + the callback is called one time for each row of the result set + and once more, at the end, passed only `null`, to indicate that + the end of the result set has been reached. Note that the rows + arrive via worker-posted messages, with all the implications + of that. + + + TODO?: a config option which causes it to queue up events to fire + one at a time and flush the event queue on the first error. The + main use for this is test runs which must fail at the first error. +*/ +self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){ + // Inspired by: https://stackoverflow.com/a/52439530 + let idNumber = 0; + const handlerMap = Object.create(null); + const noop = function(){}; + const err = config.onerror || noop; + const debug = config.debug || noop; + const genMsgId = config.generateMessageId || function(msg){ + return msg.type+'#'+(++idNumber); + }; + const toss = (...args)=>{throw new Error(args.join(' '))}; + if('function'===typeof config.worker) config.worker = config.worker(); + config.worker.onmessage = function(ev){ + ev = ev.data; + debug('worker1.onmessage',ev); + let msgHandler = handlerMap[ev.messageId]; + if(!msgHandler){ + if(ev && 'sqlite3-api'===ev.type && 'worker1-ready'===ev.result) { + /*fired one time when the Worker1 API initializes*/ + if(config.onready) config.onready(); + return; + } + msgHandler = handlerMap[ev.type] /* check for exec per-row callback */; + if(msgHandler && msgHandler.onrow){ + msgHandler.onrow(ev.row); + return; + } + if(config.onunhandled) config.onunhandled(arguments[0]); + else err("sqlite3Worker1Promiser() unhandled worker message:",ev); + return; + } + delete handlerMap[ev.messageId]; + switch(ev.type){ + case 'error': + msgHandler.reject(ev); + return; + case 'open': + if(!config.dbId) config.dbId = ev.dbId; + break; + case 'close': + if(config.dbId === ev.dbId) config.dbId = undefined; + break; + default: + break; + } + msgHandler.resolve(ev); + }/*worker.onmessage()*/; + return function(/*(msgType, msgArgs) || (msg)*/){ + let msg; + if(1===arguments.length){ + msg = arguments[0]; + }else if(2===arguments.length){ + msg = { + type: arguments[0], + args: arguments[1] + }; + }else{ + toss("Invalid arugments for sqlite3Worker1Promiser()-created factory."); + } + if(!msg.dbId) msg.dbId = config.dbId; + msg.messageId = genMsgId(msg); + msg.departureTime = performance.now(); + const proxy = Object.create(null); + proxy.message = msg; + let cbId /* message handler ID for exec on-row callback proxy */; + if('exec'===msg.type && msg.args){ + if('function'===typeof msg.args.callback){ + cbId = genMsgId(msg)+':row'; + proxy.onrow = msg.args.callback; + msg.args.callback = cbId; + handlerMap[cbId] = proxy; + }else if('string' === typeof msg.args.callback){ + toss("exec callback may not be a string when using the Promise interface."); + } + } + //debug("requestWork", msg); + const p = new Promise(function(resolve, reject){ + proxy.resolve = resolve; + proxy.reject = reject; + handlerMap[msg.messageId] = proxy; + debug("Posting",msg.type,"message to Worker dbId="+(config.dbId||'default')+':',msg); + config.worker.postMessage(msg); + }); + if(cbId) p.finally(()=>delete handlerMap[cbId]); + return p; + }; +}/*sqlite3Worker1Promiser()*/; +self.sqlite3Worker1Promiser.defaultConfig = { + worker: ()=>new Worker('sqlite3-worker1.js'), + onerror: console.error.bind(console), + dbId: undefined +}; diff --git a/ext/wasm/testing-worker-promise.js b/ext/wasm/testing-worker-promise.js deleted file mode 100644 index c60c6c2882..0000000000 --- a/ext/wasm/testing-worker-promise.js +++ /dev/null @@ -1,139 +0,0 @@ -/* - 2022-08-23 - - The author disclaims copyright to this source code. In place of a - legal notice, here is a blessing: - - * May you do good and not evil. - * May you find forgiveness for yourself and forgive others. - * May you share freely, never taking more than you give. - - *********************************************************************** - - - UNDER CONSTRUCTION: a Promise-based proxy for for the sqlite3 Worker - #1 API. -*/ -'use strict'; -(function(){ - const T = self.SqliteTestUtil; - const DbState = { - id: undefined - }; - const eOutput = document.querySelector('#test-output'); - const log = console.log.bind(console); - const logHtml = async function(cssClass,...args){ - log.apply(this, args); - const ln = document.createElement('div'); - if(cssClass) ln.classList.add(cssClass); - ln.append(document.createTextNode(args.join(' '))); - eOutput.append(ln); - }; - const warn = console.warn.bind(console); - const error = console.error.bind(console); - - let startTime; - const logEventResult = async function(evd){ - logHtml(evd.errorClass ? 'error' : '', - "response to",evd.messageId,"Worker time =", - (evd.workerRespondTime - evd.workerReceivedTime),"ms.", - "Round-trip event time =", - (performance.now() - evd.departureTime),"ms.", - (evd.errorClass ? evd.message : "") - ); - }; - - const testCount = async ()=>{ - logHtml("","Total test count:",T.counter+". Total time =",(performance.now() - startTime),"ms"); - }; - - // Inspiration: https://stackoverflow.com/a/52439530 - const worker = new Worker("sqlite3-worker1.js"); - worker.onerror = function(event){ - error("worker.onerror",event); - }; - const WorkerPromiseHandler = Object.create(null); - WorkerPromiseHandler.nextId = function f(){ - return 'msg#'+(f._ = (f._ || 0) + 1); - }; - - /** Posts a worker message as {type:eventType, data:eventData}. */ - const requestWork = async function(eventType, eventData){ - //log("requestWork", eventType, eventData); - T.assert(eventData && 'object'===typeof eventData); - /* ^^^ that is for the testing and messageId-related code, not - a hard requirement of all of the Worker-exposed APIs. */ - const wph = WorkerPromiseHandler; - const msgId = wph.nextId(); - const proxy = wph[msgId] = Object.create(null); - proxy.promise = new Promise(function(resolve, reject){ - proxy.resolve = resolve; - proxy.reject = reject; - const msg = { - type: eventType, - args: eventData, - dbId: DbState.id, - messageId: msgId, - departureTime: performance.now() - }; - log("Posting",eventType,"message to worker dbId="+(DbState.id||'default')+':',msg); - worker.postMessage(msg); - }); - log("Set up promise",proxy); - return proxy.promise; - }; - - - const runOneTest = async function(eventType, eventData, callback){ - T.assert(eventData && 'object'===typeof eventData); - /* ^^^ that is for the testing and messageId-related code, not - a hard requirement of all of the Worker-exposed APIs. */ - let p = requestWork(eventType, eventData); - if(callback) p.then(callback).finally(testCount); - return p; - }; - - const runTests = async function(){ - logHtml('', - "Sending 'open' message and waiting for its response before continuing."); - startTime = performance.now(); - runOneTest('open', { - filename:'testing2.sqlite3', - simulateError: 0 /* if true, fail the 'open' */ - }, function(ev){ - log("then open result",ev); - T.assert('testing2.sqlite3'===ev.result.filename) - .assert(ev.dbId) - .assert(ev.messageId) - .assert(DbState.id === ev.dbId); - }).catch((err)=>error("error response:",err)); - }; - - worker.onmessage = function(ev){ - ev = ev.data; - (('error'===ev.type) ? error : log)('worker.onmessage',ev); - const msgHandler = WorkerPromiseHandler[ev.messageId]; - if(!msgHandler){ - if('worker1-ready'===ev.result) { - /*sqlite3-api/worker1-ready is fired when the Worker1 API initializes*/ - self.sqlite3TestModule.setStatus(null)/*hide the HTML-side is-loading spinner*/; - runTests(); - return; - } - error("Unhandled worker message:",ev); - return; - } - logEventResult(ev); - delete WorkerPromiseHandler[ev.messageId]; - if('error'===ev.type){ - msgHandler.reject(ev); - } - else{ - if(!DbState.id && ev.dbId) DbState.id = ev.dbId; - msgHandler.resolve(ev); // async, so testCount() results on next line are out of order - //testCount(); - } - }; - - log("Init complete, but async init bits may still be running."); -})(); diff --git a/ext/wasm/testing-worker-promise.html b/ext/wasm/testing-worker1-promiser.html similarity index 91% rename from ext/wasm/testing-worker-promise.html rename to ext/wasm/testing-worker1-promiser.html index eab30c301f..9af809d9ed 100644 --- a/ext/wasm/testing-worker-promise.html +++ b/ext/wasm/testing-worker1-promiser.html @@ -28,6 +28,7 @@
- + + diff --git a/ext/wasm/testing-worker1-promiser.js b/ext/wasm/testing-worker1-promiser.js new file mode 100644 index 0000000000..9475df411a --- /dev/null +++ b/ext/wasm/testing-worker1-promiser.js @@ -0,0 +1,214 @@ +/* + 2022-08-23 + + The author disclaims copyright to this source code. In place of a + legal notice, here is a blessing: + + * May you do good and not evil. + * May you find forgiveness for yourself and forgive others. + * May you share freely, never taking more than you give. + + *********************************************************************** + + Demonstration of the sqlite3 Worker API #1 Promiser: a Promise-based + proxy for for the sqlite3 Worker #1 API. +*/ +'use strict'; +(function(){ + const T = self.SqliteTestUtil; + const eOutput = document.querySelector('#test-output'); + const warn = console.warn.bind(console); + const error = console.error.bind(console); + const log = console.log.bind(console); + const logHtml = async function(cssClass,...args){ + log.apply(this, args); + const ln = document.createElement('div'); + if(cssClass) ln.classList.add(cssClass); + ln.append(document.createTextNode(args.join(' '))); + eOutput.append(ln); + }; + + let startTime; + const logEventResult = async function(evd){ + logHtml(evd.errorClass ? 'error' : '', + "response to",evd.messageId,"Worker time =", + (evd.workerRespondTime - evd.workerReceivedTime),"ms.", + "Round-trip event time =", + (performance.now() - evd.departureTime),"ms.", + (evd.errorClass ? evd.message : "") + ); + }; + + const testCount = async ()=>{ + logHtml("","Total test count:",T.counter+". Total time =",(performance.now() - startTime),"ms"); + }; + + //why is this triggered even when we catch() a Promise? + //window.addEventListener('unhandledrejection', function(event) { + // warn('unhandledrejection',event); + //}); + + const promiserConfig = { + worker: ()=>{ + const w = new Worker("sqlite3-worker1.js"); + w.onerror = (event)=>error("worker.onerror",event); + return w; + }, + //debug: (...args)=>console.debug('worker debug',...args), + onunhandled: function(ev){ + error("Unhandled worker message:",ev.data); + }, + onready: function(){ + self.sqlite3TestModule.setStatus(null)/*hide the HTML-side is-loading spinner*/; + runTests(); + }, + onerror: function(ev){ + error("worker1 error:",ev); + } + }; + const workerPromise = self.sqlite3Worker1Promiser(promiserConfig); + delete self.sqlite3Worker1Promiser; + + const wtest = async function(msgType, msgArgs, callback){ + let p = workerPromise({type: msgType, args:msgArgs}); + if(callback) p.then(callback).finally(testCount); + return p; + }; + + const runTests = async function(){ + logHtml('', + "Sending 'open' message and waiting for its response before continuing."); + startTime = performance.now(); + wtest('open', { + filename:'testing2.sqlite3', + simulateError: 0 /* if true, fail the 'open' */ + }, function(ev){ + log("then open result",ev); + T.assert('testing2.sqlite3'===ev.result.filename) + .assert(ev.dbId) + .assert(ev.messageId) + .assert(promiserConfig.dbId === ev.dbId); + }).then(runTests2) + .catch((err)=>error("error response:",err)); + }; + + const runTests2 = async function(){ + const mustNotReach = ()=>toss("This is not supposed to be reached."); + + await wtest('exec',{ + sql: ["create table t(a,b)", + "insert into t(a,b) values(1,2),(3,4),(5,6)" + ].join(';'), + multi: true, + resultRows: [], columnNames: [] + }, function(ev){ + ev = ev.result; + T.assert(0===ev.resultRows.length) + .assert(0===ev.columnNames.length); + }); + + await wtest('exec',{ + sql: 'select a a, b b from t order by a', + resultRows: [], columnNames: [], + }, function(ev){ + ev = ev.result; + T.assert(3===ev.resultRows.length) + .assert(1===ev.resultRows[0][0]) + .assert(6===ev.resultRows[2][1]) + .assert(2===ev.columnNames.length) + .assert('b'===ev.columnNames[1]); + }); + + await wtest('exec',{ + sql: 'select a a, b b from t order by a', + resultRows: [], columnNames: [], + rowMode: 'object' + }, function(ev){ + ev = ev.result; + T.assert(3===ev.resultRows.length) + .assert(1===ev.resultRows[0].a) + .assert(6===ev.resultRows[2].b) + }); + + await wtest( + 'exec', + {sql:'intentional_error'}, + mustNotReach + ).catch((e)=>{ + warn("Intentional error:",e); + // Why does the browser report console.error "Uncaught (in + // promise)" when we catch(), and does so _twice_ if we don't + // catch()? According to all docs, that error must be supressed + // if we explicitly catch(). + }); + + await wtest('exec',{ + sql:'select 1 union all select 3', + resultRows: [], + //rowMode: 'array', // array is the default in the Worker interface + }, function(ev){ + ev = ev.result; + T.assert(2 === ev.resultRows.length) + .assert(1 === ev.resultRows[0][0]) + .assert(3 === ev.resultRows[1][0]); + }); + + const resultRowTest1 = function f(row){ + if(undefined === f.counter) f.counter = 0; + if(row) ++f.counter; + //log("exec() result row:",row); + T.assert(null===row || 'number' === typeof row.b); + }; + await wtest('exec',{ + sql: 'select a a, b b from t order by a', + callback: resultRowTest1, + rowMode: 'object' + }, function(ev){ + T.assert(3===resultRowTest1.counter); + resultRowTest1.counter = 0; + }); + + await wtest('exec',{ + multi: true, + sql:[ + 'pragma foreign_keys=0;', + // ^^^ arbitrary query with no result columns + 'select a, b from t order by a desc; select a from t;' + // multi-exec only honors results from the first + // statement with result columns (regardless of whether) + // it has any rows). + ], + rowMode: 1, + resultRows: [] + },function(ev){ + const rows = ev.result.resultRows; + T.assert(3===rows.length). + assert(6===rows[0]); + }); + + await wtest('exec',{sql: 'delete from t where a>3'}); + + await wtest('exec',{ + sql: 'select count(a) from t', + resultRows: [] + },function(ev){ + ev = ev.result; + T.assert(1===ev.resultRows.length) + .assert(2===ev.resultRows[0][0]); + }); + + /***** close() tests must come last. *****/ + await wtest('close',{unlink:true},function(ev){ + T.assert(!promiserConfig.dbId); + T.assert('string' === typeof ev.result.filename); + }); + + await wtest('close').then((ev)=>{ + T.assert(undefined === ev.result.filename); + log("That's all, folks!"); + }); + }/*runTests2()*/; + + + log("Init complete, but async init bits may still be running."); +})(); diff --git a/manifest b/manifest index d26d33dea6..05095d6b81 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\svery\sbasics\sof\sa\sPromise-based\sproxy\sfor\sthe\sWorker\s#1\sAPI.\sStill\srequires\sconsiderable\scleanup,\stesting,\sand\sa\ssolution\sfor\sthe\sexec-callback-via-event-type-name\sproblem. -D 2022-08-24T00:51:39.887 +C More\swork\son\show\sto\sconfigure\sthe\ssqlite3\sJS\sAPI\sbootstrapping\sprocess\sfrom\shigher-level\scode.\sInitial\sversion\sof\ssqlite3-worker1-promiser,\sa\sPromise-based\sproxy\sfor\sthe\sWorker\sAPI\s#1. +D 2022-08-24T05:59:23.851 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -481,12 +481,12 @@ F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de F ext/wasm/api/README.md d876597edd2b9542b6ea031adaaff1c042076fde7b670b1dc6d8a87b28a6631b F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b -F ext/wasm/api/sqlite3-api-cleanup.js acf798ce96285c0d52738466a96c9deb9d66647f711a40caecab90b5ce66ac3c +F ext/wasm/api/sqlite3-api-cleanup.js 4c353bdc2452623f0c1c1e55ae1a0589db9cbaed9756760bb15179ef9b58bc98 F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3 F ext/wasm/api/sqlite3-api-oo1.js f6dcaac3270182471f97efcfda25bd4a4ac1777b8ec52ebd1c6846721160e54c F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 -F ext/wasm/api/sqlite3-api-prologue.js 6e0e7787ed955ea2b6158e0bb7608f63b54236847700d183e49e1f10d0525b8f -F ext/wasm/api/sqlite3-api-worker1.js c9e4edb89f41a4fa65d136ae180c1bc0beb694eb95f7d9e6936fbb702914c160 +F ext/wasm/api/sqlite3-api-prologue.js 4a279604272851696975837534739597206c0800c8ea78810fe8e211ee101374 +F ext/wasm/api/sqlite3-api-worker1.js 9691e144a77490f482caa2c0f0bd38a8f955c6dc9c10b2f39c6491e817aefd8c F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247 @@ -508,9 +508,10 @@ F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb43 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0 +F ext/wasm/sqlite3-worker1-promiser.js 291f89330bc856e7ef8a321b4891554633c6407b52efc69c9b1d1b3e7c69d4a6 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e -F ext/wasm/testing-worker-promise.html ba3d5423cfbdc96c332af3632dfcb61527ba8fd7e487b3bf3f07542f890c3e08 -F ext/wasm/testing-worker-promise.js c05c46a3a22b1910f6a1db11f3da6df701259eaa1277ddba085247b7f9059423 +F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 w ext/wasm/testing-worker-promise.html +F ext/wasm/testing-worker1-promiser.js 3c13fda53cc8b5d148ae34f621eba99aff393d66718b216bfd9d3f9075dd83bc w ext/wasm/testing-worker-promise.js F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409 F ext/wasm/testing1.js 2def7a86c52ff28b145cb86188d5c7a49d5993f9b78c50d140e1c31551220955 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 @@ -2008,8 +2009,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 03b9db9b98cb36faa7de5a8a64d2e13c4aeaadfefb33ac92bb41056f6be3f121 -R 5b16a785d8c414a8eb9d618c8d9d8cea +P 1e447849fb65887e806e3348a8a68f70ea6802bc0a1e56c385a279f27cc0cdda +R 20d6ad983c84af7c7f2e33fe283b134d U stephan -Z fdaa5cf9f1bfd4215c6ebf07223819c1 +Z 996279c2387a16066b296229c9c99a7d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1b823c2233..26a4257f52 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1e447849fb65887e806e3348a8a68f70ea6802bc0a1e56c385a279f27cc0cdda \ No newline at end of file +b030f321bd5a38cdd5d6f6735f201afa62d30d2b0ba02e67f055b4895553a878 \ No newline at end of file From 9c765e7945bfe79ac724e8eaf9103914f0557271 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 24 Aug 2022 14:50:10 +0000 Subject: [PATCH 026/428] js: resolve the mysterious "extra" unhandled exception notification, caused by inadvertently forking one promise into two separate ones (failing to properly reassign a then() result). Fix a typo in new Worker 1 code which caused the DB(filename) name to be incorrect. FossilOrigin-Name: 7467ac88801224089b51c6ba7924f93283dd87beca602a186c83632df26cfc85 --- ext/wasm/api/sqlite3-api-cleanup.js | 2 +- ext/wasm/api/sqlite3-api-prologue.js | 2 +- ext/wasm/api/sqlite3-api-worker1.js | 2 +- ext/wasm/sqlite3-worker1-promiser.js | 18 ++++++++++------- ext/wasm/testing-worker1-promiser.js | 30 ++++++++++------------------ manifest | 22 ++++++++++---------- manifest.uuid | 2 +- 7 files changed, 37 insertions(+), 41 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-cleanup.js b/ext/wasm/api/sqlite3-api-cleanup.js index ed6b8c40ea..01aba213ed 100644 --- a/ext/wasm/api/sqlite3-api-cleanup.js +++ b/ext/wasm/api/sqlite3-api-cleanup.js @@ -21,7 +21,7 @@ if('undefined' !== typeof Module){ // presumably an Emscripten build Install a suitable default configuration for sqlite3ApiBootstrap(). */ const SABC = self.sqlite3ApiBootstrap.defaultConfig; - SABC.Module = Module /* ==> Current needs to be exposed here for test code. NOT part + SABC.Module = Module /* ==> Currently needs to be exposed here for test code. NOT part of the public API. */; SABC.exports = Module['asm']; SABC.memory = Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */; diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index d92b948fce..5217cfcde3 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -173,7 +173,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( config[k] = config[k](); } }); - + /** Throws a new Error, the message of which is the concatenation all args with a space between each. */ const toss = (...args)=>{throw new Error(args.join(' '))}; diff --git a/ext/wasm/api/sqlite3-api-worker1.js b/ext/wasm/api/sqlite3-api-worker1.js index 39263a4abe..c63ab1117e 100644 --- a/ext/wasm/api/sqlite3-api-worker1.js +++ b/ext/wasm/api/sqlite3-api-worker1.js @@ -180,7 +180,7 @@ sqlite3.initWorker1API = function(){ toss("Throwing because of simulateError flag."); } if(args.persistent && args.filename){ - oargs.filaname = sqlite3.capi.sqlite3_web_persistent_dir() + args.filename; + oargs.filename = sqlite3.capi.sqlite3_web_persistent_dir() + args.filename; }else if('' === args.filename){ oargs.filename = args.filename; }else{ diff --git a/ext/wasm/sqlite3-worker1-promiser.js b/ext/wasm/sqlite3-worker1-promiser.js index c01ed9a5c6..d023f86366 100644 --- a/ext/wasm/sqlite3-worker1-promiser.js +++ b/ext/wasm/sqlite3-worker1-promiser.js @@ -18,7 +18,7 @@ slightly simpler client-side interface than the slightly-lower-level Worker API does. - This script necessarily exposes on global symbol, but clients may + This script necessarily exposes one global symbol, but clients may freely `delete` that symbol after calling it. */ 'use strict'; @@ -81,8 +81,8 @@ information about messages. - This function returns a stateful factory function with the following - interfaces: + This function returns a stateful factory function with the + following interfaces: - Promise function(messageType, messageArgs) - Promise function({message object}) @@ -183,7 +183,11 @@ self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){ default: break; } - msgHandler.resolve(ev); + try { + msgHandler.resolve(ev); + }catch(e){ + msgHandler.reject(e); + } }/*worker.onmessage()*/; return function(/*(msgType, msgArgs) || (msg)*/){ let msg; @@ -214,19 +218,19 @@ self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){ } } //debug("requestWork", msg); - const p = new Promise(function(resolve, reject){ + let p = new Promise(function(resolve, reject){ proxy.resolve = resolve; proxy.reject = reject; handlerMap[msg.messageId] = proxy; debug("Posting",msg.type,"message to Worker dbId="+(config.dbId||'default')+':',msg); config.worker.postMessage(msg); }); - if(cbId) p.finally(()=>delete handlerMap[cbId]); + if(cbId) p = p.finally(()=>delete handlerMap[cbId]); return p; }; }/*sqlite3Worker1Promiser()*/; self.sqlite3Worker1Promiser.defaultConfig = { worker: ()=>new Worker('sqlite3-worker1.js'), - onerror: console.error.bind(console), + onerror: (...args)=>console.error('worker1 error',...args), dbId: undefined }; diff --git a/ext/wasm/testing-worker1-promiser.js b/ext/wasm/testing-worker1-promiser.js index 9475df411a..cfae443d06 100644 --- a/ext/wasm/testing-worker1-promiser.js +++ b/ext/wasm/testing-worker1-promiser.js @@ -29,16 +29,6 @@ }; let startTime; - const logEventResult = async function(evd){ - logHtml(evd.errorClass ? 'error' : '', - "response to",evd.messageId,"Worker time =", - (evd.workerRespondTime - evd.workerReceivedTime),"ms.", - "Round-trip event time =", - (performance.now() - evd.departureTime),"ms.", - (evd.errorClass ? evd.message : "") - ); - }; - const testCount = async ()=>{ logHtml("","Total test count:",T.counter+". Total time =",(performance.now() - startTime),"ms"); }; @@ -70,26 +60,28 @@ delete self.sqlite3Worker1Promiser; const wtest = async function(msgType, msgArgs, callback){ - let p = workerPromise({type: msgType, args:msgArgs}); - if(callback) p.then(callback).finally(testCount); - return p; + const p = workerPromise({type: msgType, args:msgArgs}); + return callback ? p.then(callback).finally(testCount) : p; }; const runTests = async function(){ + const dbFilename = '/testing2.sqlite3'; logHtml('', "Sending 'open' message and waiting for its response before continuing."); startTime = performance.now(); - wtest('open', { - filename:'testing2.sqlite3', - simulateError: 0 /* if true, fail the 'open' */ + await wtest('open', { + filename: dbFilename, + persistent: true, + simulateError: 0 /* if true, fail the 'open' */, }, function(ev){ log("then open result",ev); - T.assert('testing2.sqlite3'===ev.result.filename) + T.assert(1 && (dbFilename===ev.result.filename + || (sqlite3TestModule.sqlite3ApiConfig.persistentDirName + + dbFilename)==ev.result.filename)) .assert(ev.dbId) .assert(ev.messageId) .assert(promiserConfig.dbId === ev.dbId); - }).then(runTests2) - .catch((err)=>error("error response:",err)); + }).then(runTests2); }; const runTests2 = async function(){ diff --git a/manifest b/manifest index 05095d6b81..ca5ed7147e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\swork\son\show\sto\sconfigure\sthe\ssqlite3\sJS\sAPI\sbootstrapping\sprocess\sfrom\shigher-level\scode.\sInitial\sversion\sof\ssqlite3-worker1-promiser,\sa\sPromise-based\sproxy\sfor\sthe\sWorker\sAPI\s#1. -D 2022-08-24T05:59:23.851 +C js:\sresolve\sthe\smysterious\s"extra"\sunhandled\sexception\snotification,\scaused\sby\sinadvertently\sforking\sone\spromise\sinto\stwo\sseparate\sones\s(failing\sto\sproperly\sreassign\sa\sthen()\sresult).\sFix\sa\stypo\sin\snew\sWorker\s1\scode\swhich\scaused\sthe\sDB(filename)\sname\sto\sbe\sincorrect. +D 2022-08-24T14:50:10.920 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -481,12 +481,12 @@ F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de F ext/wasm/api/README.md d876597edd2b9542b6ea031adaaff1c042076fde7b670b1dc6d8a87b28a6631b F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b -F ext/wasm/api/sqlite3-api-cleanup.js 4c353bdc2452623f0c1c1e55ae1a0589db9cbaed9756760bb15179ef9b58bc98 +F ext/wasm/api/sqlite3-api-cleanup.js 1a12e64060c2cb0defd34656a76a9b1d7ed58459c290249bb31567c806fd44de F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3 F ext/wasm/api/sqlite3-api-oo1.js f6dcaac3270182471f97efcfda25bd4a4ac1777b8ec52ebd1c6846721160e54c F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 -F ext/wasm/api/sqlite3-api-prologue.js 4a279604272851696975837534739597206c0800c8ea78810fe8e211ee101374 -F ext/wasm/api/sqlite3-api-worker1.js 9691e144a77490f482caa2c0f0bd38a8f955c6dc9c10b2f39c6491e817aefd8c +F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db98a17685ab7a34a03953c7a +F ext/wasm/api/sqlite3-api-worker1.js b23f66ef5afd350a17fbadb795007098e518a40e5c7c439cd83ef34aa55a45af F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247 @@ -508,10 +508,10 @@ F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb43 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0 -F ext/wasm/sqlite3-worker1-promiser.js 291f89330bc856e7ef8a321b4891554633c6407b52efc69c9b1d1b3e7c69d4a6 +F ext/wasm/sqlite3-worker1-promiser.js 9638b0ced7f02806c3220b616f08729dde9eb13fb56e125cd4759f40bfa81210 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e -F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 w ext/wasm/testing-worker-promise.html -F ext/wasm/testing-worker1-promiser.js 3c13fda53cc8b5d148ae34f621eba99aff393d66718b216bfd9d3f9075dd83bc w ext/wasm/testing-worker-promise.js +F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 +F ext/wasm/testing-worker1-promiser.js 931d909c769c57292f1cafdf10c7dab402d17cd16a6d0ec32089f67b559b058f F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409 F ext/wasm/testing1.js 2def7a86c52ff28b145cb86188d5c7a49d5993f9b78c50d140e1c31551220955 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 @@ -2009,8 +2009,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 1e447849fb65887e806e3348a8a68f70ea6802bc0a1e56c385a279f27cc0cdda -R 20d6ad983c84af7c7f2e33fe283b134d +P b030f321bd5a38cdd5d6f6735f201afa62d30d2b0ba02e67f055b4895553a878 +R a11ec2b23afafa9cff8838dfde907f0c U stephan -Z 996279c2387a16066b296229c9c99a7d +Z 64f88270da37ddd3ce54e6e07b8ab1af # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 26a4257f52..521621a6a1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b030f321bd5a38cdd5d6f6735f201afa62d30d2b0ba02e67f055b4895553a878 \ No newline at end of file +7467ac88801224089b51c6ba7924f93283dd87beca602a186c83632df26cfc85 \ No newline at end of file From 3734401a95dc92f7cb7c3a86875370f1598213aa Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 24 Aug 2022 18:39:46 +0000 Subject: [PATCH 027/428] Expand the worker1 'exec' op handling for per-row callbacks for API-level consistency and smooth some edges between worker1 core and worker1-promiser. Add worker1 'config-get' message to fetch the serializable parts of the sqlite3.config state. Improve the 'open' op's handling of the 'persistent' option (noting that we cannot yet test that case from a worker). FossilOrigin-Name: 509f8839201ec1ea4863bd31493e6c29a0721ca6340755bb96656b828758fea7 --- ext/wasm/api/sqlite3-api-oo1.js | 15 ++ ext/wasm/api/sqlite3-api-worker1.js | 244 +++++++++++++++++---------- ext/wasm/sqlite3-worker1-promiser.js | 63 ++++--- ext/wasm/testing-worker1-promiser.js | 50 ++++-- ext/wasm/testing2.js | 12 +- manifest | 20 +-- manifest.uuid | 2 +- 7 files changed, 264 insertions(+), 142 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index be9d8af5a8..e16b45bb5e 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -252,6 +252,21 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ out.cbArg = (stmt)=>stmt.get(out.opt.rowMode); break; } + /* + TODO?: how can we define rowMode such that it uses + rowMode of 'object' and returns a given named field from + the object. Something like: + + if(?what goes here?){ + out.cbArg = function f(stmt){return stmt.get(this.obj)[this.colName]} + .bind({obj:{}, colName: ???what goes here???}}); + break; + } + + Maybe rowMode:['colName1',... 'colNameN']? That could be + ambiguous: might mean "return an object with just these + columns". + */ toss3("Invalid rowMode:",out.opt.rowMode); } } diff --git a/ext/wasm/api/sqlite3-api-worker1.js b/ext/wasm/api/sqlite3-api-worker1.js index c63ab1117e..afb2e78124 100644 --- a/ext/wasm/api/sqlite3-api-worker1.js +++ b/ext/wasm/api/sqlite3-api-worker1.js @@ -42,25 +42,21 @@ initialization is complete, as the initialization is synchronous. In some contexts, however, listening for the above message is a better fit. + + Note that the worker-based interface can be slightly quirky because + of its async nature. In particular, any number of messages may be posted + to the worker before it starts handling any of them. If, e.g., an + "open" operation fails, any subsequent messages will fail. The + Promise-based wrapper for this API (`sqlite3-worker1-promiser.js`) + is more comfortable to use in that regard. + + + TODO: hoist the message API docs from deep in this code to here. + */ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ sqlite3.initWorker1API = function(){ 'use strict'; - /** - UNDER CONSTRUCTION - - We need an API which can proxy the DB API via a Worker message - interface. The primary quirky factor in such an API is that we - cannot pass callback functions between the window thread and a - worker thread, so we have to receive all db results via - asynchronous message-passing. That requires an asychronous API - with a distinctly different shape than OO API #1. - - TODOs include, but are not necessarily limited to: - - - Support for handling multiple DBs via this interface is under - development. - */ const toss = (...args)=>{throw new Error(args.join(' '))}; if('function' !== typeof importScripts){ toss("Cannot initalize the sqlite3 worker API in the main thread."); @@ -86,12 +82,13 @@ sqlite3.initWorker1API = function(){ }; /** - Helper for managing Worker-level state. + Internal helper for managing Worker-level state. */ const wState = { defaultDb: undefined, idSeq: 0, idMap: new WeakMap, + xfer: [/*Temp holder for "transferable" postMessage() state.*/], open: function(opt){ const db = new DB(opt.filename); this.dbs[getDbId(db)] = db; @@ -109,9 +106,15 @@ sqlite3.initWorker1API = function(){ } } }, + /** + Posts the given worker message value. If xferList is provided, + it must be an array, in which case a copy of it passed as + postMessage()'s second argument and xferList.length is set to + 0. + */ post: function(msg,xferList){ - if(xferList){ - self.postMessage( msg, xferList ); + if(xferList && xferList.length){ + self.postMessage( msg, Array.from(xferList) ); xferList.length = 0; }else{ self.postMessage(msg); @@ -146,32 +149,40 @@ sqlite3.initWorker1API = function(){ message type key. The onmessage() dispatcher attempts to dispatch all inbound messages to a method of this object, passing it the event.data part of the inbound event object. All - methods must return a plain Object containing any response + methods must return a plain Object containing any result state, which the dispatcher may amend. All methods must throw on error. */ const wMsgHandler = { - xfer: [/*Temp holder for "transferable" postMessage() state.*/], /** Proxy for the DB constructor. Expects to be passed a single - object or a falsy value to use defaults. The object may have a - filename property to name the db file (see the DB constructor - for peculiarities and transformations). The response is an - object: + object or a falsy value to use defaults: { - filename: db filename (possibly differing from the input), + filename [=":memory:" or "" (unspecified)]: the db filename. + See the sqlite3.oo1.DB constructor for peculiarities and transformations, + + persistent [=false]: if true and filename is not one of ("", + ":memory:"), prepend + sqlite3.capi.sqlite3_web_persistent_dir() to the given + filename so that it is stored in persistent storage _if_ the + environment supports it. If persistent storage is not + supported, the filename is used as-is. + } + + The response object looks like: + + { + filename: db filename, possibly differing from the input. dbId: an opaque ID value which must be passed in the message - envelope to other calls in this API to tell them which - db to use. If it is not provided to future calls, they - will default to operating on the first-opened db. + envelope to other calls in this API to tell them which db to + use. If it is not provided to future calls, they will default + to operating on the first-opened db. - persistent: prepend sqlite3.capi.sqlite3_web_persistent_dir() - to the given filename so that it is stored - in persistent storage _if_ the environment supports it. - If persistent storage is not supported, the filename - is used as-is. + persistent: true if the given filename resides in the + known-persistent storage, else false. This determination is + independent of the `persistent` input argument. } */ open: function(ev){ @@ -179,28 +190,32 @@ sqlite3.initWorker1API = function(){ if(args.simulateError){ // undocumented internal testing option toss("Throwing because of simulateError flag."); } - if(args.persistent && args.filename){ - oargs.filename = sqlite3.capi.sqlite3_web_persistent_dir() + args.filename; - }else if('' === args.filename){ - oargs.filename = args.filename; + const rc = Object.create(null); + const pDir = sqlite3.capi.sqlite3_web_persistent_dir(); + if(!args.filename || ':memory:'===args.filename){ + oargs.filename = args.filename || ''; + }else if(pDir){ + oargs.filename = pDir + ('/'===args.filename[0] ? args.filename : ('/'+args.filename)); }else{ - oargs.filename = args.filename || ':memory:'; - } + oargs.filename = args.filename; + } const db = wState.open(oargs); - return { - filename: db.filename, - dbId: getDbId(db) - }; + rc.filename = db.filename; + rc.persistent = !!pDir && db.filename.startsWith(pDir); + rc.dbId = getDbId(db); + return rc; }, /** Proxy for DB.close(). ev.args may be elided or an object with an `unlink` property. If that value is truthy then the db file (if the db is currently open) will be unlinked from the virtual - filesystem, else it will be kept intact. The result object is: + filesystem, else it will be kept intact, noting that unlink + failure is ignored. The result object is: { filename: db filename _if_ the db is opened when this - is called, else the undefined value + is called, else the undefined value + dbId: the ID of the closed b, or undefined if none is closed } @@ -211,7 +226,7 @@ sqlite3.initWorker1API = function(){ const db = getMsgDb(ev,false); const response = { filename: db && db.filename, - dbId: db ? getDbId(db) : undefined + dbId: db && getDbId(db) }; if(db){ wState.close(db, ((ev.args && 'object'===typeof ev.args) @@ -220,7 +235,7 @@ sqlite3.initWorker1API = function(){ return response; }, /** - Proxy for DB.exec() which expects a single argument of type + Proxy for oo1.DB.exec() which expects a single argument of type string (SQL to execute) or an options object in the form expected by exec(). The notable differences from exec() include: @@ -234,12 +249,21 @@ sqlite3.initWorker1API = function(){ message type key, in which case a callback function will be applied which posts each row result via: - postMessage({type: thatKeyType, row: theRow}) + postMessage({type: thatKeyType, rowNumber: 1-based-#, row: theRow}) - And, at the end of the result set (whether or not any - result rows were produced), it will post an identical - message with row:null to alert the caller than the result - set is completed. + And, at the end of the result set (whether or not any result + rows were produced), it will post an identical message with + (row=undefined, rowNumber=null) to alert the caller than the + result set is completed. Note that a row value of `null` is + a legal row result for certain `rowMode` values. + + (Design note: we don't use (row=undefined, rowNumber=undefined) + to indicate end-of-results because fetching those would be + indistinguishable from fetching from an empty object unless the + client used hasOwnProperty() (or similar) to distinguish + "missing property" from "property with the undefined value". + Similarly, `null` is a legal value for `row` in some case , + whereas the db layer won't emit a result value of `undefined`.) The callback proxy must not recurse into this interface, or results are undefined. (It hypothetically cannot recurse @@ -250,52 +274,73 @@ sqlite3.initWorker1API = function(){ The response is the input options object (or a synthesized one if passed only a string), noting that options.resultRows and options.columnNames may be populated - by the call to exec(). - - This opens/creates the Worker's db if needed. + by the call to db.exec(). */ exec: function(ev){ - const opt = ( + const rc = ( 'string'===typeof ev.args ) ? {sql: ev.args} : (ev.args || Object.create(null)); - if(undefined===opt.rowMode){ + if(undefined===rc.rowMode){ /* Since the default rowMode of 'stmt' is not useful for the Worker interface, we'll default to something else. */ - opt.rowMode = 'array'; - }else if('stmt'===opt.rowMode){ - toss("Invalid rowMode for exec(): stmt mode", + rc.rowMode = 'array'; + }else if('stmt'===rc.rowMode){ + toss("Invalid rowMode for 'exec': stmt mode", "does not work in the Worker API."); } const db = getMsgDb(ev); - if(opt.callback || Array.isArray(opt.resultRows)){ + if(rc.callback || Array.isArray(rc.resultRows)){ // Part of a copy-avoidance optimization for blobs - db._blobXfer = this.xfer; + db._blobXfer = wState.xfer; } - const callbackMsgType = opt.callback; + const callbackMsgType = rc.callback; + let rowNumber = 0; if('string' === typeof callbackMsgType){ /* Treat this as a worker message type and post each row as a message of that type. */ - const that = this; - opt.callback = - (row)=>wState.post({type: callbackMsgType, row:row}, this.xfer); + rc.callback = + (row)=>wState.post({type: callbackMsgType, rowNumber:++rowNumber, row:row}, wState.xfer); } try { - db.exec(opt); - if(opt.callback instanceof Function){ - opt.callback = callbackMsgType; - wState.post({type: callbackMsgType, row: null}); + db.exec(rc); + if(rc.callback instanceof Function){ + rc.callback = callbackMsgType; + wState.post({type: callbackMsgType, rowNumber: null, row: undefined}); } - }/*catch(e){ - console.warn("Worker is propagating:",e);throw e; - }*/finally{ - delete db._blobXfer; - if(opt.callback){ - opt.callback = callbackMsgType; - } - } - return opt; + }finally{ + delete db._blobXfer; + if(rc.callback){ + rc.callback = callbackMsgType; + } + } + return rc; }/*exec()*/, + /** + Returns a JSON-friendly form of a _subset_ of sqlite3.config, + sans any parts which cannot be serialized. Because we cannot, + from here, distingush whether or not certain objects can be + serialized, this routine selectively copies certain properties + rather than trying JSON.stringify() and seeing what happens + (the results are horrid if the config object contains an + Emscripten module object). + + In addition to the "real" config properties, it sythesizes + the following: + + - persistenceEnabled: true if persistent dir support is available, + else false. + */ + 'config-get': function(){ + const rc = Object.create(null), src = sqlite3.config; + [ + 'persistentDirName', 'bigIntEnabled' + ].forEach(function(k){ + if(Object.getOwnPropertyDescriptor(src, k)) rc[k] = src[k]; + }); + rc.persistenceEnabled = !!sqlite3.capi.sqlite3_web_persistent_dir(); + return rc; + }, /** TO(RE)DO, once we can abstract away access to the JS environment's virtual filesystem. Currently this @@ -329,7 +374,7 @@ sqlite3.initWorker1API = function(){ filename: db.filename, mimetype: 'application/x-sqlite3' }; - this.xfer.push(response.buffer.buffer); + wState.xfer.push(response.buffer.buffer); return response;**/ }/*export()*/, toss: function(ev){ @@ -344,22 +389,32 @@ sqlite3.initWorker1API = function(){ form: { type: apiCommand, - dbId: optional DB ID value (else uses a default db handle), args: apiArguments, + dbId: optional DB ID value (else uses a default db handle), messageId: optional client-specific value } As a rule, these commands respond with a postMessage() of their - own in the form: - - TODO: refactoring is underway. - - The responses always have an object-format `result` part. If the - inbound object has a `messageId` property, that property is + own. The responses always have a `type` property equal to the + input message's type and an object-format `result` part. If + the inbound object has a `messageId` property, that property is always mirrored in the result object, for use in client-side - dispatching of these asynchronous results. Exceptions thrown - during processing result in an `error`-type event with a payload - in the form: + dispatching of these asynchronous results. For example: + + { + type: 'open', + messageId: ...copied from inbound message..., + dbId: ID of db which was opened, + result: { + dbId: repeat of ^^^, for API consistency's sake, + filename: ..., + persistent: false + }, + ...possibly other framework-internal/testing/debugging info... + } + + Exceptions thrown during processing result in an `error`-type + event with a payload in the form: { type: 'error', dbId: DB handle ID, @@ -413,10 +468,15 @@ sqlite3.initWorker1API = function(){ workerReceivedTime: arrivalTime, workerRespondTime: performance.now(), departureTime: ev.departureTime, + // TODO: move the timing bits into... + //timing:{ + // departure: ev.departureTime, + // workerReceived: arrivalTime, + // workerResponse: performance.now(); + //}, result: result - }, wMsgHandler.xfer); + }, wState.xfer); }; self.postMessage({type:'sqlite3-api',result:'worker1-ready'}); }.bind({self, sqlite3}); }); - diff --git a/ext/wasm/sqlite3-worker1-promiser.js b/ext/wasm/sqlite3-worker1-promiser.js index d023f86366..7327e14c70 100644 --- a/ext/wasm/sqlite3-worker1-promiser.js +++ b/ext/wasm/sqlite3-worker1-promiser.js @@ -27,7 +27,7 @@ manipulated via a Promise-based interface and returns a factory function which returns Promises for communicating with the worker. This proxy has an _almost_ identical interface to the normal - worker API, with any exceptions noted below. + worker API, with any exceptions documented below. It requires a configuration object with the following properties: @@ -127,26 +127,34 @@ - exec's {callback: STRING} option does not work via this interface (it triggers an exception), but {callback: function} does and works exactly like the STRING form does in the Worker: - the callback is called one time for each row of the result set - and once more, at the end, passed only `null`, to indicate that + the callback is called one time for each row of the result set, + passed the same worker message format as the worker API emits: + + {type:typeString, row:VALUE, rowNumber:1-based-#} + + Where `typeString` is an internally-synthesized message type string + used temporarily for worker message dispatching. It can be ignored + by all client code except that which tests this API. The `row` + property contains the row result in the form implied by the + `rowMode` option (defaulting to `'array'`). The `rowNumber` is a + 1-based integer value incremented by 1 on each call into th + callback. + + At the end of the result set, the same event is fired with + (row=undefined, rowNumber=null) to indicate that the end of the result set has been reached. Note that the rows arrive via worker-posted messages, with all the implications of that. - - - TODO?: a config option which causes it to queue up events to fire - one at a time and flush the event queue on the first error. The - main use for this is test runs which must fail at the first error. */ self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){ // Inspired by: https://stackoverflow.com/a/52439530 - let idNumber = 0; const handlerMap = Object.create(null); const noop = function(){}; const err = config.onerror || noop; const debug = config.debug || noop; + const idTypeMap = config.generateMessageId ? undefined : Object.create(null); const genMsgId = config.generateMessageId || function(msg){ - return msg.type+'#'+(++idNumber); + return msg.type+'#'+(idTypeMap[msg.type] = (idTypeMap[msg.type]||0) + 1); }; const toss = (...args)=>{throw new Error(args.join(' '))}; if('function'===typeof config.worker) config.worker = config.worker(); @@ -162,7 +170,7 @@ self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){ } msgHandler = handlerMap[ev.type] /* check for exec per-row callback */; if(msgHandler && msgHandler.onrow){ - msgHandler.onrow(ev.row); + msgHandler.onrow(ev); return; } if(config.onunhandled) config.onunhandled(arguments[0]); @@ -183,13 +191,10 @@ self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){ default: break; } - try { - msgHandler.resolve(ev); - }catch(e){ - msgHandler.reject(e); - } + try {msgHandler.resolve(ev)} + catch(e){msgHandler.reject(e)} }/*worker.onmessage()*/; - return function(/*(msgType, msgArgs) || (msg)*/){ + return function(/*(msgType, msgArgs) || (msgEnvelope)*/){ let msg; if(1===arguments.length){ msg = arguments[0]; @@ -206,15 +211,29 @@ self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){ msg.departureTime = performance.now(); const proxy = Object.create(null); proxy.message = msg; - let cbId /* message handler ID for exec on-row callback proxy */; + let rowCallbackId /* message handler ID for exec on-row callback proxy */; if('exec'===msg.type && msg.args){ if('function'===typeof msg.args.callback){ - cbId = genMsgId(msg)+':row'; + rowCallbackId = msg.messageId+':row'; proxy.onrow = msg.args.callback; - msg.args.callback = cbId; - handlerMap[cbId] = proxy; + msg.args.callback = rowCallbackId; + handlerMap[rowCallbackId] = proxy; }else if('string' === typeof msg.args.callback){ toss("exec callback may not be a string when using the Promise interface."); + /** + Design note: the reason for this limitation is that this + API takes over worker.onmessage() and the client has no way + of adding their own message-type handlers to it. Per-row + callbacks are implemented as short-lived message.type + mappings for worker.onmessage(). + + We "could" work around this by providing a new + config.fallbackMessageHandler (or some such) which contains + a map of event type names to callbacks. Seems like overkill + for now, seeing as the client can pass callback functions + to this interface (whereas the string-form "callback" is + needed for the over-the-Worker interface). + */ } } //debug("requestWork", msg); @@ -225,7 +244,7 @@ self.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){ debug("Posting",msg.type,"message to Worker dbId="+(config.dbId||'default')+':',msg); config.worker.postMessage(msg); }); - if(cbId) p = p.finally(()=>delete handlerMap[cbId]); + if(rowCallbackId) p = p.finally(()=>delete handlerMap[rowCallbackId]); return p; }; }/*sqlite3Worker1Promiser()*/; diff --git a/ext/wasm/testing-worker1-promiser.js b/ext/wasm/testing-worker1-promiser.js index cfae443d06..a2f3a2523d 100644 --- a/ext/wasm/testing-worker1-promiser.js +++ b/ext/wasm/testing-worker1-promiser.js @@ -60,24 +60,41 @@ delete self.sqlite3Worker1Promiser; const wtest = async function(msgType, msgArgs, callback){ + if(2===arguments.length && 'function'===typeof msgArgs){ + callback = msgArgs; + msgArgs = undefined; + } const p = workerPromise({type: msgType, args:msgArgs}); return callback ? p.then(callback).finally(testCount) : p; }; const runTests = async function(){ const dbFilename = '/testing2.sqlite3'; - logHtml('', - "Sending 'open' message and waiting for its response before continuing."); startTime = performance.now(); + + let sqConfig; + await wtest('config-get', (ev)=>{ + const r = ev.result; + log('sqlite3.config subset:', r); + T.assert('boolean' === typeof r.bigIntEnabled) + .assert('string'===typeof r.persistentDirName) + .assert('boolean' === typeof r.persistenceEnabled); + sqConfig = r; + }); + logHtml('', + "Sending 'open' message and waiting for its response before continuing..."); + await wtest('open', { filename: dbFilename, - persistent: true, + persistent: sqConfig.persistenceEnabled, simulateError: 0 /* if true, fail the 'open' */, }, function(ev){ - log("then open result",ev); - T.assert(1 && (dbFilename===ev.result.filename - || (sqlite3TestModule.sqlite3ApiConfig.persistentDirName - + dbFilename)==ev.result.filename)) + const r = ev.result; + log("then open result",r); + T.assert(r.persistent === sqConfig.persistenceEnabled) + .assert(r.persistent + ? (dbFilename!==r.filename) + : (dbFilename==r.filename)) .assert(ev.dbId) .assert(ev.messageId) .assert(promiserConfig.dbId === ev.dbId); @@ -145,11 +162,17 @@ .assert(3 === ev.resultRows[1][0]); }); - const resultRowTest1 = function f(row){ + const resultRowTest1 = function f(ev){ if(undefined === f.counter) f.counter = 0; - if(row) ++f.counter; - //log("exec() result row:",row); - T.assert(null===row || 'number' === typeof row.b); + if(null === ev.rowNumber){ + /* End of result set. */ + T.assert(undefined === ev.row); + }else{ + T.assert(ev.rowNumber > 0); + ++f.counter; + } + log("exec() result row:",ev); + T.assert(null === ev.rowNumber || 'number' === typeof ev.row.b); }; await wtest('exec',{ sql: 'select a a, b b from t order by a', @@ -195,10 +218,9 @@ T.assert('string' === typeof ev.result.filename); }); - await wtest('close').then((ev)=>{ + await wtest('close', (ev)=>{ T.assert(undefined === ev.result.filename); - log("That's all, folks!"); - }); + }).finally(()=>log("That's all, folks!")); }/*runTests2()*/; diff --git a/ext/wasm/testing2.js b/ext/wasm/testing2.js index b051cc04cc..d64fd6e89a 100644 --- a/ext/wasm/testing2.js +++ b/ext/wasm/testing2.js @@ -115,9 +115,15 @@ }, resultRowTest1: function f(ev){ if(undefined === f.counter) f.counter = 0; - if(ev.row) ++f.counter; - //log("exec() result row:",ev.row); - T.assert(null===ev.row || 'number' === typeof ev.row.b); + if(null === ev.rowNumber){ + /* End of result set. */ + T.assert(undefined === ev.row); + }else{ + T.assert(ev.rowNumber > 0); + ++f.counter; + } + //log("exec() result row:",ev); + T.assert(null === ev.rowNumber || 'number' === typeof ev.row.b); } }; diff --git a/manifest b/manifest index ca5ed7147e..2b3fc24b37 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C js:\sresolve\sthe\smysterious\s"extra"\sunhandled\sexception\snotification,\scaused\sby\sinadvertently\sforking\sone\spromise\sinto\stwo\sseparate\sones\s(failing\sto\sproperly\sreassign\sa\sthen()\sresult).\sFix\sa\stypo\sin\snew\sWorker\s1\scode\swhich\scaused\sthe\sDB(filename)\sname\sto\sbe\sincorrect. -D 2022-08-24T14:50:10.920 +C Expand\sthe\sworker1\s'exec'\sop\shandling\sfor\sper-row\scallbacks\sfor\sAPI-level\sconsistency\sand\ssmooth\ssome\sedges\sbetween\sworker1\score\sand\sworker1-promiser.\sAdd\sworker1\s'config-get'\smessage\sto\sfetch\sthe\sserializable\sparts\sof\sthe\ssqlite3.config\sstate.\sImprove\sthe\s'open'\sop's\shandling\sof\sthe\s'persistent'\soption\s(noting\sthat\swe\scannot\syet\stest\sthat\scase\sfrom\sa\sworker). +D 2022-08-24T18:39:46.246 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -483,10 +483,10 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 1a12e64060c2cb0defd34656a76a9b1d7ed58459c290249bb31567c806fd44de F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3 -F ext/wasm/api/sqlite3-api-oo1.js f6dcaac3270182471f97efcfda25bd4a4ac1777b8ec52ebd1c6846721160e54c +F ext/wasm/api/sqlite3-api-oo1.js 5ce93b89165e1eb6ef26ba67ae9d3c25379df74eea82edb7b46255f86db21cfc F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db98a17685ab7a34a03953c7a -F ext/wasm/api/sqlite3-api-worker1.js b23f66ef5afd350a17fbadb795007098e518a40e5c7c439cd83ef34aa55a45af +F ext/wasm/api/sqlite3-api-worker1.js f7372b84b6d71ebdc0d2a9e7944ce571b4f18e0dd4c1be78282c68b4582558ca F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247 @@ -508,14 +508,14 @@ F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb43 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0 -F ext/wasm/sqlite3-worker1-promiser.js 9638b0ced7f02806c3220b616f08729dde9eb13fb56e125cd4759f40bfa81210 +F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 -F ext/wasm/testing-worker1-promiser.js 931d909c769c57292f1cafdf10c7dab402d17cd16a6d0ec32089f67b559b058f +F ext/wasm/testing-worker1-promiser.js 81d81eda77c9d4a3e43cfeee91df6c3b039782cc998020d72fe1fdf91790242d F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409 F ext/wasm/testing1.js 2def7a86c52ff28b145cb86188d5c7a49d5993f9b78c50d140e1c31551220955 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 -F ext/wasm/testing2.js f01e8d7b32f6d4790f8352bf8dc17d2b860afa58f6c8ea1f824ef1c0d2068138 +F ext/wasm/testing2.js 04a4194188d54856027eb4cad7239223a8f7a60e64b0aac81fc1a5a70363b98e F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2009,8 +2009,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P b030f321bd5a38cdd5d6f6735f201afa62d30d2b0ba02e67f055b4895553a878 -R a11ec2b23afafa9cff8838dfde907f0c +P 7467ac88801224089b51c6ba7924f93283dd87beca602a186c83632df26cfc85 +R 4d53a1266e528a954f73a792c3648256 U stephan -Z 64f88270da37ddd3ce54e6e07b8ab1af +Z cffa6daad399e2d401f0fd69f4f04b4f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 521621a6a1..7400cbe177 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7467ac88801224089b51c6ba7924f93283dd87beca602a186c83632df26cfc85 \ No newline at end of file +509f8839201ec1ea4863bd31493e6c29a0721ca6340755bb96656b828758fea7 \ No newline at end of file From 407f75378e2bfebfd21ca56b6986154f0c35d1ac Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 24 Aug 2022 20:57:37 +0000 Subject: [PATCH 028/428] Change DB.exec() rowMode default from 'stmt' to 'array', per /chat discussion. Add DB.exec() rowMode option for fetching a specific column by name. Add result column names to worker1 exec() callback interface, as there's otherwise no way to get that info from a worker. FossilOrigin-Name: 1bb37e5c477b9eb098362f74a45a55be23d450fe45cdff58c1cbff08b5b3998f --- ext/wasm/api/sqlite3-api-oo1.js | 83 ++++++++++++++++------------ ext/wasm/api/sqlite3-api-worker1.js | 46 +++++++++------ ext/wasm/testing-worker1-promiser.js | 53 ++++++++++++++++-- ext/wasm/testing2.js | 4 +- manifest | 18 +++--- manifest.uuid | 2 +- 6 files changed, 139 insertions(+), 67 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index e16b45bb5e..ea42e6bf8d 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -236,14 +236,14 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } if(out.opt.callback || out.opt.resultRows){ switch((undefined===out.opt.rowMode) - ? 'stmt' : out.opt.rowMode) { + ? 'array' : out.opt.rowMode) { case 'object': out.cbArg = (stmt)=>stmt.get({}); break; case 'array': out.cbArg = (stmt)=>stmt.get([]); break; case 'stmt': if(Array.isArray(out.opt.resultRows)){ - toss3("Invalid rowMode for resultRows array: must", + toss3("Invalid rowMode for a resultRows array: must", "be one of 'array', 'object',", - "or a result column number."); + "a result column number, or column name reference."); } out.cbArg = (stmt)=>stmt; break; @@ -251,22 +251,16 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ if(util.isInt32(out.opt.rowMode)){ out.cbArg = (stmt)=>stmt.get(out.opt.rowMode); break; - } - /* - TODO?: how can we define rowMode such that it uses - rowMode of 'object' and returns a given named field from - the object. Something like: - - if(?what goes here?){ - out.cbArg = function f(stmt){return stmt.get(this.obj)[this.colName]} - .bind({obj:{}, colName: ???what goes here???}}); + }else if('string'===typeof out.opt.rowMode && out.opt.rowMode.length>1){ + /* "$X", ":X", and "@X" fetch column named "X" (case-sensitive!) */ + const prefix = out.opt.rowMode[0]; + if(':'===prefix || '@'===prefix || '$'===prefix){ + out.cbArg = function(stmt){ + return stmt.get(this.obj)[this.colName]; + }.bind({obj:{}, colName: out.opt.rowMode.substr(1)}) break; } - - Maybe rowMode:['colName1',... 'colNameN']? That could be - ambiguous: might mean "return an object with just these - columns". - */ + } toss3("Invalid rowMode:",out.opt.rowMode); } } @@ -449,7 +443,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ if(rowTarget) rowTarget.push(row); if(opt.callback){ stmt._isLocked = true; - opt.callback(row, stmt); + opt.callback(row,stmt); stmt._isLocked = false; } } @@ -494,23 +488,40 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ if that statement has any result _rows_. The callback's "this" is the options object. 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 ... + may collect column names, or similar). The 2nd argument to the + callback is always the Stmt instance, as it's needed if the + caller wants to fetch the column names or some such. The first + argument passed to the callback defaults to the current Stmt + object but may be changed with ... - - .rowMode = either a string describing what type of argument - should be passed as the first argument to the callback or an - integer representing a result column index. A `rowMode` of - 'object' causes the results of `stmt.get({})` to be passed to - the `callback` and/or appended to `resultRows`. A value of - 'array' causes the results of `stmt.get([])` to be passed to - passed on. 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), but this mode will trigger - an exception if `resultRows` is an array. If `rowMode` is an - integer, only the single value from that result column will be - passed on. Any other value for the option triggers an - exception. + - .rowMode = may take one of several forms: + + A) If `rowMode` is an integer, only the single value from that + result column (0-based) will be passed on. + + B) A string describing what type of argument should be passed + as the first argument to the callback: + + B.1) 'array' (the default) causes the results of + `stmt.get([])` to be passed to passed on and/or appended to + `resultRows`. + + B.2) 'object' causes the results of `stmt.get({})` to be + passed to the `callback` and/or appended to `resultRows`. + + B.3) 'stmt' causes the current Stmt to be passed to the + callback, but this mode will trigger an exception if + `resultRows` is an array because appending the statement to + the array would be unhelpful. + + C) A string with a minimum length of 2 and leading character of + ':', '$', or '@' will fetch the row as an object, extract that + one field, and pass that field's value to the callback. Note + that these keys are case-sensitive so must match the case used + in the SQL. e.g. `"select a A from t"` with a `rowMode` of '$A' + would work but '$a' would not (it would result in `undefined`). + + Any other `rowMode` value triggers an exception. - .resultRows: if this is an array, it functions similarly to the `callback` option: each row of the result set (if any) of @@ -584,7 +595,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ else wasm.jstrcpy(arg.sql, wasm.heap8(), pSql, sqlByteLen, false); wasm.setMemValue(pSql + sqlByteLen, 0/*NUL terminator*/); while(wasm.getMemValue(pSql, 'i8') - /* Maintenance reminder: ^^^^ _must_ be i8 or else we + /* Maintenance reminder:^^^ _must_ be 'i8' or else we will very likely cause an endless loop. What that's doing is checking for a terminating NUL byte. If we use i32 or similar then we read 4 bytes, read stuff @@ -615,7 +626,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ stmt._isLocked = true; const row = arg.cbArg(stmt); if(resultRows) resultRows.push(row); - if(callback) callback(row, stmt); + if(callback) callback(row,stmt); stmt._isLocked = false; } rowMode = undefined; diff --git a/ext/wasm/api/sqlite3-api-worker1.js b/ext/wasm/api/sqlite3-api-worker1.js index afb2e78124..90c8f0de19 100644 --- a/ext/wasm/api/sqlite3-api-worker1.js +++ b/ext/wasm/api/sqlite3-api-worker1.js @@ -249,7 +249,11 @@ sqlite3.initWorker1API = function(){ message type key, in which case a callback function will be applied which posts each row result via: - postMessage({type: thatKeyType, rowNumber: 1-based-#, row: theRow}) + postMessage({type: thatKeyType, + rowNumber: 1-based-#, + row: theRow, + columnNames: anArray + }) And, at the end of the result set (whether or not any result rows were produced), it will post an identical message with @@ -280,12 +284,7 @@ sqlite3.initWorker1API = function(){ const rc = ( 'string'===typeof ev.args ) ? {sql: ev.args} : (ev.args || Object.create(null)); - if(undefined===rc.rowMode){ - /* Since the default rowMode of 'stmt' is not useful - for the Worker interface, we'll default to - something else. */ - rc.rowMode = 'array'; - }else if('stmt'===rc.rowMode){ + if('stmt'===rc.rowMode){ toss("Invalid rowMode for 'exec': stmt mode", "does not work in the Worker API."); } @@ -294,25 +293,40 @@ sqlite3.initWorker1API = function(){ // Part of a copy-avoidance optimization for blobs db._blobXfer = wState.xfer; } - const callbackMsgType = rc.callback; + const theCallback = rc.callback; let rowNumber = 0; - if('string' === typeof callbackMsgType){ + const hadColNames = !!rc.columnNames; + if('string' === typeof theCallback){ + if(!hadColNames) rc.columnNames = []; /* Treat this as a worker message type and post each row as a message of that type. */ - rc.callback = - (row)=>wState.post({type: callbackMsgType, rowNumber:++rowNumber, row:row}, wState.xfer); + rc.callback = function(row,stmt){ + wState.post({ + type: theCallback, + columnNames: rc.columnNames, + rowNumber: ++rowNumber, + row: row + }, wState.xfer); + } } try { db.exec(rc); if(rc.callback instanceof Function){ - rc.callback = callbackMsgType; - wState.post({type: callbackMsgType, rowNumber: null, row: undefined}); + rc.callback = theCallback; + /* Post a sentinel message to tell the client that the end + of the result set has been reached (possibly with zero + rows). */ + wState.post({ + type: theCallback, + columnNames: rc.columnNames, + rowNumber: null /*null to distinguish from "property not set"*/, + row: undefined /*undefined because null is a legal row value + for some rowType values, but undefined is not*/ + }); } }finally{ delete db._blobXfer; - if(rc.callback){ - rc.callback = callbackMsgType; - } + if(rc.callback) rc.callback = theCallback; } return rc; }/*exec()*/, diff --git a/ext/wasm/testing-worker1-promiser.js b/ext/wasm/testing-worker1-promiser.js index a2f3a2523d..63401033c0 100644 --- a/ext/wasm/testing-worker1-promiser.js +++ b/ext/wasm/testing-worker1-promiser.js @@ -166,16 +166,19 @@ if(undefined === f.counter) f.counter = 0; if(null === ev.rowNumber){ /* End of result set. */ - T.assert(undefined === ev.row); + T.assert(undefined === ev.row) + .assert(2===ev.columnNames.length) + .assert('a'===ev.columnNames[0]) + .assert('B'===ev.columnNames[1]); }else{ T.assert(ev.rowNumber > 0); ++f.counter; } log("exec() result row:",ev); - T.assert(null === ev.rowNumber || 'number' === typeof ev.row.b); + T.assert(null === ev.rowNumber || 'number' === typeof ev.row.B); }; await wtest('exec',{ - sql: 'select a a, b b from t order by a', + sql: 'select a a, b B from t order by a limit 3', callback: resultRowTest1, rowMode: 'object' }, function(ev){ @@ -183,6 +186,48 @@ resultRowTest1.counter = 0; }); + const resultRowTest2 = function f(ev){ + if(null === ev.rowNumber){ + /* End of result set. */ + T.assert(undefined === ev.row) + .assert(1===ev.columnNames.length) + .assert('a'===ev.columnNames[0]) + }else{ + T.assert(ev.rowNumber > 0); + f.counter = ev.rowNumber; + } + log("exec() result row:",ev); + T.assert(null === ev.rowNumber || 'number' === typeof ev.row); + }; + await wtest('exec',{ + sql: 'select a a from t limit 3', + callback: resultRowTest2, + rowMode: 0 + }, function(ev){ + T.assert(3===resultRowTest2.counter); + }); + + const resultRowTest3 = function f(ev){ + if(null === ev.rowNumber){ + T.assert(3===ev.columnNames.length) + .assert('foo'===ev.columnNames[0]) + .assert('bar'===ev.columnNames[1]) + .assert('baz'===ev.columnNames[2]); + }else{ + f.counter = ev.rowNumber; + T.assert('number' === typeof ev.row); + } + }; + await wtest('exec',{ + sql: "select 'foo' foo, a bar, 'baz' baz from t limit 2", + callback: resultRowTest3, + columnNames: [], + rowMode: ':bar' + }, function(ev){ + log("exec() result row:",ev); + T.assert(2===resultRowTest3.counter); + }); + await wtest('exec',{ multi: true, sql:[ @@ -220,7 +265,7 @@ await wtest('close', (ev)=>{ T.assert(undefined === ev.result.filename); - }).finally(()=>log("That's all, folks!")); + }).finally(()=>logHtml('',"That's all, folks!")); }/*runTests2()*/; diff --git a/ext/wasm/testing2.js b/ext/wasm/testing2.js index d64fd6e89a..8d9e66dc1f 100644 --- a/ext/wasm/testing2.js +++ b/ext/wasm/testing2.js @@ -117,7 +117,9 @@ if(undefined === f.counter) f.counter = 0; if(null === ev.rowNumber){ /* End of result set. */ - T.assert(undefined === ev.row); + T.assert(undefined === ev.row) + .assert(Array.isArray(ev.columnNames)) + .assert(ev.columnNames.length); }else{ T.assert(ev.rowNumber > 0); ++f.counter; diff --git a/manifest b/manifest index 2b3fc24b37..dd1b396898 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Expand\sthe\sworker1\s'exec'\sop\shandling\sfor\sper-row\scallbacks\sfor\sAPI-level\sconsistency\sand\ssmooth\ssome\sedges\sbetween\sworker1\score\sand\sworker1-promiser.\sAdd\sworker1\s'config-get'\smessage\sto\sfetch\sthe\sserializable\sparts\sof\sthe\ssqlite3.config\sstate.\sImprove\sthe\s'open'\sop's\shandling\sof\sthe\s'persistent'\soption\s(noting\sthat\swe\scannot\syet\stest\sthat\scase\sfrom\sa\sworker). -D 2022-08-24T18:39:46.246 +C Change\sDB.exec()\srowMode\sdefault\sfrom\s'stmt'\sto\s'array',\sper\s/chat\sdiscussion.\sAdd\sDB.exec()\srowMode\soption\sfor\sfetching\sa\sspecific\scolumn\sby\sname.\sAdd\sresult\scolumn\snames\sto\sworker1\sexec()\scallback\sinterface,\sas\sthere's\sotherwise\sno\sway\sto\sget\sthat\sinfo\sfrom\sa\sworker. +D 2022-08-24T20:57:37.430 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -483,10 +483,10 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 1a12e64060c2cb0defd34656a76a9b1d7ed58459c290249bb31567c806fd44de F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3 -F ext/wasm/api/sqlite3-api-oo1.js 5ce93b89165e1eb6ef26ba67ae9d3c25379df74eea82edb7b46255f86db21cfc +F ext/wasm/api/sqlite3-api-oo1.js 324b2f6817ff3711b59bd9505157f7a91fe319249d3b8b525c8254427c10504a F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db98a17685ab7a34a03953c7a -F ext/wasm/api/sqlite3-api-worker1.js f7372b84b6d71ebdc0d2a9e7944ce571b4f18e0dd4c1be78282c68b4582558ca +F ext/wasm/api/sqlite3-api-worker1.js 11a9e8b22147d948e338b25d21697178b4414dc0578fc9613aa5fc4bfe62f208 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247 @@ -511,11 +511,11 @@ F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d169404 F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 -F ext/wasm/testing-worker1-promiser.js 81d81eda77c9d4a3e43cfeee91df6c3b039782cc998020d72fe1fdf91790242d +F ext/wasm/testing-worker1-promiser.js f4b0895b612606d04ae371d03a9ffe9ffa94a2a840da6e92742b2adf86f0783c F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409 F ext/wasm/testing1.js 2def7a86c52ff28b145cb86188d5c7a49d5993f9b78c50d140e1c31551220955 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 -F ext/wasm/testing2.js 04a4194188d54856027eb4cad7239223a8f7a60e64b0aac81fc1a5a70363b98e +F ext/wasm/testing2.js ab4ae24cd3ffe814370b35515aea426647a6f9d271c6542cf18e580470540615 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2009,8 +2009,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 7467ac88801224089b51c6ba7924f93283dd87beca602a186c83632df26cfc85 -R 4d53a1266e528a954f73a792c3648256 +P 509f8839201ec1ea4863bd31493e6c29a0721ca6340755bb96656b828758fea7 +R 4d9ca8433788ec1804950abdc80ba467 U stephan -Z cffa6daad399e2d401f0fd69f4f04b4f +Z aac659391ac05e725777e807ed0e05f4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7400cbe177..c547adbe4a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -509f8839201ec1ea4863bd31493e6c29a0721ca6340755bb96656b828758fea7 \ No newline at end of file +1bb37e5c477b9eb098362f74a45a55be23d450fe45cdff58c1cbff08b5b3998f \ No newline at end of file From 9afff9f3c539516c29ce0752b5b84adf8f6ebc19 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 25 Aug 2022 11:39:12 +0000 Subject: [PATCH 029/428] Refactor and expand the worker1 docs, consolidating them into the top of their file instead of scattered around the internals. Accommodate an API change from yesterday in demo-oo1.js. FossilOrigin-Name: 0a65747047322b7b585e281ac275e437ce3f46e1d06105c19117213929a906ad --- ext/wasm/api/sqlite3-api-oo1.js | 2 +- ext/wasm/api/sqlite3-api-worker1.js | 449 +++++++++++++++++---------- ext/wasm/demo-oo1.js | 4 +- ext/wasm/testing-worker1-promiser.js | 4 +- manifest | 18 +- manifest.uuid | 2 +- 6 files changed, 303 insertions(+), 176 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index ea42e6bf8d..0d2416a282 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -412,7 +412,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ Stmt.getColumnNames() to append the column names to it (regardless of whether the query produces any result rows). If the query has no result columns, this value is - unchanged. + unchanged. (TODO: support this in execMulti() as well.) The following options to execMulti() are _not_ supported by this method (they are simply ignored): diff --git a/ext/wasm/api/sqlite3-api-worker1.js b/ext/wasm/api/sqlite3-api-worker1.js index 90c8f0de19..f882f6d76a 100644 --- a/ext/wasm/api/sqlite3-api-worker1.js +++ b/ext/wasm/api/sqlite3-api-worker1.js @@ -22,19 +22,20 @@ */ /** - This function implements a Worker-based wrapper around SQLite3 OO - API #1, colloquially known as "Worker API #1". + sqlite3.initWorker1API() implements a Worker-based wrapper around + SQLite3 OO API #1, colloquially known as "Worker API #1". In order to permit this API to be loaded in worker threads without automatically registering onmessage handlers, initializing the - worker API requires calling initWorker1API(). If this function - is called from a non-worker thread then it throws an exception. + worker API requires calling initWorker1API(). If this function is + called from a non-worker thread then it throws an exception. It + must only be called once per Worker. When initialized, it installs message listeners to receive Worker messages and then it posts a message in the form: ``` - {type:'sqlite3-api',result:'worker1-ready'} + {type:'sqlite3-api', result:'worker1-ready'} ``` to let the client know that it has been initialized. Clients may @@ -50,8 +51,275 @@ Promise-based wrapper for this API (`sqlite3-worker1-promiser.js`) is more comfortable to use in that regard. + The documentation for the input and output worker messages for + this API follows... - TODO: hoist the message API docs from deep in this code to here. + ==================================================================== + Common message format... + + Each message posted to the worker has an operation-independent + envelope and operation-dependent arguments: + + ``` + { + type: string, // one of: 'open', 'close', 'exec', 'config-get' + + messageId: OPTIONAL arbitrary value. The worker will copy it as-is + into response messages to assist in client-side dispatching. + + dbId: a db identifier string (returned by 'open') which tells the + operation which database instance to work on. If not provided, the + first-opened db is used. This is an "opaque" value, with no + inherently useful syntax or information. Its value is subject to + change with any given build of this API and cannot be used as a + basis for anything useful beyond its one intended purpose. + + args: ...operation-dependent arguments... + + // the framework may add other properties for testing or debugging + // purposes. + + } + ``` + + Response messages, posted back to the main thread, look like: + + ``` + { + type: string. Same as above except for error responses, which have the type + 'error', + + messageId: same value, if any, provided by the inbound message + + dbId: the id of the db which was operated on, if any, as returned + by the corresponding 'open' operation. + + result: ...operation-dependent result... + + } + ``` + + ==================================================================== + Error responses + + Errors are reported messages in an operation-independent format: + + ``` + { + type: 'error', + + messageId: ...as above..., + + dbId: ...as above... + + result: { + + operation: type of the triggering operation: 'open', 'close', ... + + message: ...error message text... + + errorClass: string. The ErrorClass.name property from the thrown exception. + + input: the message object which triggered the error. + + stack: _if available_, a stack trace array. + + } + + } + ``` + + + ==================================================================== + "config-get" + + This operation fetches the serializable parts of the sqlite3 API + configuration. + + Message format: + + ``` + { + type: "config-get", + messageId: ...as above..., + args: currently ignored and may be elided. + } + ``` + + Response: + + ``` + { + type: 'config', + messageId: ...as above..., + result: { + + persistentDirName: path prefix, if any, of persistent storage. + An empty string denotes that no persistent storage is available. + + bigIntEnabled: bool. True if BigInt support is enabled. + + persistenceEnabled: true if persistent storage is enabled in the + current environment. Only files stored under persistentDirName + will persist, however. + + } + } + ``` + + + ==================================================================== + "open" a database + + Message format: + + ``` + { + type: "open", + messageId: ...as above..., + args:{ + + filename [=":memory:" or "" (unspecified)]: the db filename. + See the sqlite3.oo1.DB constructor for peculiarities and transformations, + + persistent [=false]: if true and filename is not one of ("", + ":memory:"), prepend sqlite3.capi.sqlite3_web_persistent_dir() + to the given filename so that it is stored in persistent storage + _if_ the environment supports it. If persistent storage is not + supported, the filename is used as-is. + + } + } + ``` + + Response: + + ``` + { + type: 'open', + messageId: ...as above..., + result: { + filename: db filename, possibly differing from the input. + + dbId: an opaque ID value which must be passed in the message + envelope to other calls in this API to tell them which db to + use. If it is not provided to future calls, they will default to + operating on the first-opened db. This property is, for API + consistency's sake, also part of the contaning message envelope. + Only the `open` operation includes it in the `result` property. + + persistent: true if the given filename resides in the + known-persistent storage, else false. This determination is + independent of the `persistent` input argument. + } + } + ``` + + ==================================================================== + "close" a database + + Message format: + + ``` + { + type: "close", + messageId: ...as above... + dbId: ...as above... + args: OPTIONAL: { + + unlink: if truthy, the associated db will be unlinked (removed) + from the virtual filesystems. Failure to unlink is silently + ignored. + + } + } + ``` + + If the dbId does not refer to an opened ID, this is a no-op. The + inability to close a db (because it's not opened) or delete its + file does not trigger an error. + + Response: + + ``` + { + type: 'close', + messageId: ...as above..., + result: { + + filename: filename of closed db, or undefined if no db was closed + + } + } + ``` + + ==================================================================== + "exec" SQL + + All SQL execution is processed through the exec operation. It offers + most of the features of the oo1.DB.exec() method, with a few limitations + imposed by the state having to cross thread boundaries. + + Message format: + + ``` + { + type: "exec", + messageId: ...as above... + dbId: ...as above... + args: string (SQL) or {... see below ...} + } + ``` + + Response: + + ``` + { + type: 'exec', + messageId: ...as above..., + dbId: ...as above... + result: { + input arguments, possibly modified. See below. + } + } + ``` + + The arguments are in the same form accepted by oo1.DB.exec(), with + the exceptions noted below. + + A function-type args.callback property cannot cross + the window/Worker boundary, so is not useful here. If + args.callback is a string then it is assumed to be a + message type key, in which case a callback function will be + applied which posts each row result via: + + postMessage({type: thatKeyType, + rowNumber: 1-based-#, + row: theRow, + columnNames: anArray + }) + + And, at the end of the result set (whether or not any result rows + were produced), it will post an identical message with + (row=undefined, rowNumber=null) to alert the caller than the result + set is completed. Note that a row value of `null` is a legal row + result for certain arg.rowMode values. + + (Design note: we don't use (row=undefined, rowNumber=undefined) to + indicate end-of-results because fetching those would be + indistinguishable from fetching from an empty object unless the + client used hasOwnProperty() (or similar) to distinguish "missing + property" from "property with the undefined value". Similarly, + `null` is a legal value for `row` in some case , whereas the db + layer won't emit a result value of `undefined`.) + + The callback proxy must not recurse into this interface. An exec() + call will type up the Worker thread, causing any recursion attempt + to wait until the first exec() is completed. + + The response is the input options object (or a synthesized one if + passed only a string), noting that options.resultRows and + options.columnNames may be populated by the call to db.exec(). */ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ @@ -85,10 +353,15 @@ sqlite3.initWorker1API = function(){ Internal helper for managing Worker-level state. */ const wState = { + /** First-opened db is the default for future operations when no + dbId is provided by the client. */ defaultDb: undefined, + /** Sequence number of dbId generation. */ idSeq: 0, + /** Map of DB instances to dbId. */ idMap: new WeakMap, - xfer: [/*Temp holder for "transferable" postMessage() state.*/], + /** Temp holder for "transferable" postMessage() state. */ + xfer: [], open: function(opt){ const db = new DB(opt.filename); this.dbs[getDbId(db)] = db; @@ -122,6 +395,8 @@ sqlite3.initWorker1API = function(){ }, /** Map of DB IDs to DBs. */ dbs: Object.create(null), + /** Fetch the DB for the given id. Throw if require=true and the + id is not valid, else return the db or undefined. */ getDb: function(id,require=true){ return this.dbs[id] || (require ? toss("Unknown (or closed) DB ID:",id) : undefined); @@ -154,37 +429,6 @@ sqlite3.initWorker1API = function(){ on error. */ const wMsgHandler = { - /** - Proxy for the DB constructor. Expects to be passed a single - object or a falsy value to use defaults: - - { - filename [=":memory:" or "" (unspecified)]: the db filename. - See the sqlite3.oo1.DB constructor for peculiarities and transformations, - - persistent [=false]: if true and filename is not one of ("", - ":memory:"), prepend - sqlite3.capi.sqlite3_web_persistent_dir() to the given - filename so that it is stored in persistent storage _if_ the - environment supports it. If persistent storage is not - supported, the filename is used as-is. - } - - The response object looks like: - - { - filename: db filename, possibly differing from the input. - - dbId: an opaque ID value which must be passed in the message - envelope to other calls in this API to tell them which db to - use. If it is not provided to future calls, they will default - to operating on the first-opened db. - - persistent: true if the given filename resides in the - known-persistent storage, else false. This determination is - independent of the `persistent` input argument. - } - */ open: function(ev){ const oargs = Object.create(null), args = (ev.args || Object.create(null)); if(args.simulateError){ // undocumented internal testing option @@ -205,28 +449,11 @@ sqlite3.initWorker1API = function(){ rc.dbId = getDbId(db); return rc; }, - /** - Proxy for DB.close(). ev.args may be elided or an object with - an `unlink` property. If that value is truthy then the db file - (if the db is currently open) will be unlinked from the virtual - filesystem, else it will be kept intact, noting that unlink - failure is ignored. The result object is: - { - filename: db filename _if_ the db is opened when this - is called, else the undefined value - - dbId: the ID of the closed b, or undefined if none is closed - } - - It does not error if the given db is already closed or no db is - provided. It is simply does nothing useful in that case. - */ close: function(ev){ const db = getMsgDb(ev,false); const response = { - filename: db && db.filename, - dbId: db && getDbId(db) + filename: db && db.filename }; if(db){ wState.close(db, ((ev.args && 'object'===typeof ev.args) @@ -234,52 +461,7 @@ sqlite3.initWorker1API = function(){ } return response; }, - /** - Proxy for oo1.DB.exec() which expects a single argument of type - string (SQL to execute) or an options object in the form - expected by exec(). The notable differences from exec() - include: - - The default value for options.rowMode is 'array' because - the normal default cannot cross the window/Worker boundary. - - - A function-type options.callback property cannot cross - the window/Worker boundary, so is not useful here. If - options.callback is a string then it is assumed to be a - message type key, in which case a callback function will be - applied which posts each row result via: - - postMessage({type: thatKeyType, - rowNumber: 1-based-#, - row: theRow, - columnNames: anArray - }) - - And, at the end of the result set (whether or not any result - rows were produced), it will post an identical message with - (row=undefined, rowNumber=null) to alert the caller than the - result set is completed. Note that a row value of `null` is - a legal row result for certain `rowMode` values. - - (Design note: we don't use (row=undefined, rowNumber=undefined) - to indicate end-of-results because fetching those would be - indistinguishable from fetching from an empty object unless the - client used hasOwnProperty() (or similar) to distinguish - "missing property" from "property with the undefined value". - Similarly, `null` is a legal value for `row` in some case , - whereas the db layer won't emit a result value of `undefined`.) - - The callback proxy must not recurse into this interface, or - results are undefined. (It hypothetically cannot recurse - because an exec() call will be tying up the Worker thread, - causing any recursion attempt to wait until the first - exec() is completed.) - - The response is the input options object (or a synthesized - one if passed only a string), noting that - options.resultRows and options.columnNames may be populated - by the call to db.exec(). - */ exec: function(ev){ const rc = ( 'string'===typeof ev.args @@ -287,6 +469,8 @@ sqlite3.initWorker1API = function(){ if('stmt'===rc.rowMode){ toss("Invalid rowMode for 'exec': stmt mode", "does not work in the Worker API."); + }else if(!rc.sql){ + toss("'exec' requires input SQL."); } const db = getMsgDb(ev); if(rc.callback || Array.isArray(rc.resultRows)){ @@ -330,21 +514,7 @@ sqlite3.initWorker1API = function(){ } return rc; }/*exec()*/, - /** - Returns a JSON-friendly form of a _subset_ of sqlite3.config, - sans any parts which cannot be serialized. Because we cannot, - from here, distingush whether or not certain objects can be - serialized, this routine selectively copies certain properties - rather than trying JSON.stringify() and seeing what happens - (the results are horrid if the config object contains an - Emscripten module object). - In addition to the "real" config properties, it sythesizes - the following: - - - persistenceEnabled: true if persistent dir support is available, - else false. - */ 'config-get': function(){ const rc = Object.create(null), src = sqlite3.config; [ @@ -355,6 +525,7 @@ sqlite3.initWorker1API = function(){ rc.persistenceEnabled = !!sqlite3.capi.sqlite3_web_persistent_dir(); return rc; }, + /** TO(RE)DO, once we can abstract away access to the JS environment's virtual filesystem. Currently this @@ -391,58 +562,12 @@ sqlite3.initWorker1API = function(){ wState.xfer.push(response.buffer.buffer); return response;**/ }/*export()*/, + toss: function(ev){ toss("Testing worker exception"); } }/*wMsgHandler*/; - /** - UNDER CONSTRUCTION! - - A subset of the DB API is accessible via Worker messages in the - form: - - { type: apiCommand, - args: apiArguments, - dbId: optional DB ID value (else uses a default db handle), - messageId: optional client-specific value - } - - As a rule, these commands respond with a postMessage() of their - own. The responses always have a `type` property equal to the - input message's type and an object-format `result` part. If - the inbound object has a `messageId` property, that property is - always mirrored in the result object, for use in client-side - dispatching of these asynchronous results. For example: - - { - type: 'open', - messageId: ...copied from inbound message..., - dbId: ID of db which was opened, - result: { - dbId: repeat of ^^^, for API consistency's sake, - filename: ..., - persistent: false - }, - ...possibly other framework-internal/testing/debugging info... - } - - Exceptions thrown during processing result in an `error`-type - event with a payload in the form: - - { type: 'error', - dbId: DB handle ID, - [messageId: if set in the inbound message], - result: { - operation: "inbound message's 'type' value", - message: error string, - errorClass: class name of the error type, - input: ev.data - } - } - - The individual APIs are documented in the wMsgHandler object. - */ self.onmessage = function(ev){ ev = ev.data; let result, dbId = ev.dbId, evType = ev.type; diff --git a/ext/wasm/demo-oo1.js b/ext/wasm/demo-oo1.js index 4eff51b8e8..333d3c204c 100644 --- a/ext/wasm/demo-oo1.js +++ b/ext/wasm/demo-oo1.js @@ -136,8 +136,10 @@ db.exec({ sql: "select a, twice(a), twice(''||a) from t order by a desc limit 3", columnNames: columnNames, + rowMode: 'stmt', callback: function(row){ - log("a =",row.get(0), "twice(a) =", row.get(1), "twice(''||a) =",row.get(2)); + log("a =",row.get(0), "twice(a) =", row.get(1), + "twice(''||a) =",row.get(2)); } }); log("Result column names:",columnNames); diff --git a/ext/wasm/testing-worker1-promiser.js b/ext/wasm/testing-worker1-promiser.js index 63401033c0..4cc6547882 100644 --- a/ext/wasm/testing-worker1-promiser.js +++ b/ext/wasm/testing-worker1-promiser.js @@ -44,7 +44,7 @@ w.onerror = (event)=>error("worker.onerror",event); return w; }, - //debug: (...args)=>console.debug('worker debug',...args), + debug: 1 ? undefined : (...args)=>console.debug('worker debug',...args), onunhandled: function(ev){ error("Unhandled worker message:",ev.data); }, @@ -95,7 +95,7 @@ .assert(r.persistent ? (dbFilename!==r.filename) : (dbFilename==r.filename)) - .assert(ev.dbId) + .assert(ev.dbId === r.dbId) .assert(ev.messageId) .assert(promiserConfig.dbId === ev.dbId); }).then(runTests2); diff --git a/manifest b/manifest index dd1b396898..feef45cc58 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Change\sDB.exec()\srowMode\sdefault\sfrom\s'stmt'\sto\s'array',\sper\s/chat\sdiscussion.\sAdd\sDB.exec()\srowMode\soption\sfor\sfetching\sa\sspecific\scolumn\sby\sname.\sAdd\sresult\scolumn\snames\sto\sworker1\sexec()\scallback\sinterface,\sas\sthere's\sotherwise\sno\sway\sto\sget\sthat\sinfo\sfrom\sa\sworker. -D 2022-08-24T20:57:37.430 +C Refactor\sand\sexpand\sthe\sworker1\sdocs,\sconsolidating\sthem\sinto\sthe\stop\sof\stheir\sfile\sinstead\sof\sscattered\saround\sthe\sinternals.\sAccommodate\san\sAPI\schange\sfrom\syesterday\sin\sdemo-oo1.js. +D 2022-08-25T11:39:12.535 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -483,10 +483,10 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 1a12e64060c2cb0defd34656a76a9b1d7ed58459c290249bb31567c806fd44de F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3 -F ext/wasm/api/sqlite3-api-oo1.js 324b2f6817ff3711b59bd9505157f7a91fe319249d3b8b525c8254427c10504a +F ext/wasm/api/sqlite3-api-oo1.js a207d53bcc11955cc8f844aa59b30caaba8f57573222f62010628cfa4b1f4444 F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db98a17685ab7a34a03953c7a -F ext/wasm/api/sqlite3-api-worker1.js 11a9e8b22147d948e338b25d21697178b4414dc0578fc9613aa5fc4bfe62f208 +F ext/wasm/api/sqlite3-api-worker1.js 38db6c3d77798a0ef8be78ae6d421ef144e2e9602cdabdd7a93c7fcb7a2d449f F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247 @@ -494,7 +494,7 @@ F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 F ext/wasm/common/whwasmutil.js 41b8e097e0a9cb07c24c0ede3c81b72470a63f4a4efb07f75586dc131569f5ae F ext/wasm/demo-oo1.html 75646855b38405d82781246fd08c852a2b3bee05dd9f0fe10ab655a8cffb79aa -F ext/wasm/demo-oo1.js 04e947b64a36ed8d6fe6d5e3ccee16ffc8b4461dd186e84f4baf44d53cc3aa72 +F ext/wasm/demo-oo1.js 77b837b0fe13b542cee893c8caf84009482986bd43cf775197dfeb1e62ec0a2b F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 @@ -511,7 +511,7 @@ F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d169404 F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 -F ext/wasm/testing-worker1-promiser.js f4b0895b612606d04ae371d03a9ffe9ffa94a2a840da6e92742b2adf86f0783c +F ext/wasm/testing-worker1-promiser.js c62b5879339eef0b21aebd9d75bc125c86530edc17470afff18077f931cb704a F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409 F ext/wasm/testing1.js 2def7a86c52ff28b145cb86188d5c7a49d5993f9b78c50d140e1c31551220955 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 @@ -2009,8 +2009,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 509f8839201ec1ea4863bd31493e6c29a0721ca6340755bb96656b828758fea7 -R 4d9ca8433788ec1804950abdc80ba467 +P 1bb37e5c477b9eb098362f74a45a55be23d450fe45cdff58c1cbff08b5b3998f +R 317eb3fcfe686c9323e52473ace36982 U stephan -Z aac659391ac05e725777e807ed0e05f4 +Z 2516659ac708c3f3e5c0bf51001da2da # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c547adbe4a..31227d768b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1bb37e5c477b9eb098362f74a45a55be23d450fe45cdff58c1cbff08b5b3998f \ No newline at end of file +0a65747047322b7b585e281ac275e437ce3f46e1d06105c19117213929a906ad \ No newline at end of file From 335ad5264f497a680a5529884de419b54809089f Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 25 Aug 2022 13:27:52 +0000 Subject: [PATCH 030/428] Consolidate oo1.DB.exec() and oo1.DB.execMulti() into oo1.DB.exec(). This is a bit less efficient but certainly easier for a client to deal with and lightens the maintenance burden. FossilOrigin-Name: 7eff7213dff553b76d7ce45063e3c4a19544716611a0b609593d704076b38d0b --- ext/wasm/api/sqlite3-api-oo1.js | 243 +++++++++++----------------- ext/wasm/api/sqlite3-api-worker1.js | 2 +- ext/wasm/demo-oo1.js | 19 +-- ext/wasm/testing2.js | 18 +-- manifest | 18 +-- manifest.uuid | 2 +- 6 files changed, 117 insertions(+), 185 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 0d2416a282..c2b0601374 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -198,9 +198,9 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }; /** - Expects to be passed the `arguments` object from DB.exec() and - DB.execMulti(). Does the argument processing/validation, throws - on error, and returns a new object on success: + Expects to be passed the `arguments` object from DB.exec(). Does + the argument processing/validation, throws on error, and returns + a new object on success: { sql: the SQL, opt: optionsObj, cbArg: function} @@ -237,11 +237,11 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ if(out.opt.callback || out.opt.resultRows){ switch((undefined===out.opt.rowMode) ? 'array' : out.opt.rowMode) { - case 'object': out.cbArg = (stmt)=>stmt.get({}); break; + case 'object': out.cbArg = (stmt)=>stmt.get(Object.create(null)); break; case 'array': out.cbArg = (stmt)=>stmt.get([]); break; case 'stmt': if(Array.isArray(out.opt.resultRows)){ - toss3("Invalid rowMode for a resultRows array: must", + toss3("exec(): invalid rowMode for a resultRows array: must", "be one of 'array', 'object',", "a result column number, or column name reference."); } @@ -256,8 +256,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const prefix = out.opt.rowMode[0]; if(':'===prefix || '@'===prefix || '$'===prefix){ out.cbArg = function(stmt){ - return stmt.get(this.obj)[this.colName]; - }.bind({obj:{}, colName: out.opt.rowMode.substr(1)}) + const rc = stmt.get(this.obj)[this.colName]; + return (undefined===rc) ? toss3("exec(): unknown result column:",this.colName) : rc; + }.bind({ + obj:Object.create(null), + colName: out.opt.rowMode.substr(1) + }); break; } } @@ -394,70 +398,6 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ __stmtMap.get(this)[pStmt] = stmt; return stmt; }, - /** - This function works like execMulti(), and takes most of the - same arguments, but is more efficient (performs much less - work) when the input SQL is only a single statement. If - passed a multi-statement SQL, it only processes the first - one. - - This function supports the following additional options not - supported by execMulti(): - - - .multi: if true, this function acts as a proxy for - execMulti() and behaves identically to that function. - - - .columnNames: if this is an array and the query has - result columns, the array is passed to - Stmt.getColumnNames() to append the column names to it - (regardless of whether the query produces any result - rows). If the query has no result columns, this value is - unchanged. (TODO: support this in execMulti() as well.) - - The following options to execMulti() are _not_ supported by - this method (they are simply ignored): - - - .saveSql - */ - 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, rowTarget; - try { - if(Array.isArray(opt.resultRows)){ - rowTarget = opt.resultRows; - } - stmt = this.prepare(arg.sql); - if(stmt.columnCount && Array.isArray(opt.columnNames)){ - stmt.getColumnNames(opt.columnNames); - } - if(opt.bind) stmt.bind(opt.bind); - if(opt.callback || rowTarget){ - while(stmt.step()){ - const row = arg.cbArg(stmt); - if(rowTarget) rowTarget.push(row); - if(opt.callback){ - stmt._isLocked = true; - opt.callback(row,stmt); - stmt._isLocked = false; - } - } - }else{ - stmt.step(); - } - }finally{ - if(stmt){ - delete stmt._isLocked; - stmt.finalize(); - } - } - return this; - }/*exec()*/, /** Executes one or more SQL statements in the form of a single string. Its arguments must be either (sql,optionsObject) or @@ -473,109 +413,115 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ properties: - .sql = the SQL to run (unless it's provided as the first - argument). This must be of type string, Uint8Array, or an - array of strings (in which case they're concatenated - together as-is, with no separator between elements, - before evaluation). + argument). This must be of type string, Uint8Array, or an array + of strings. In the latter case they're concatenated together + as-is, _with no separator_ between elements, before evaluation. + The array form is often simpler for long hand-written queries. - .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.) + 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.) + + - .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. + + ================================================================== + The following options apply _only_ to the _first_ statement + which has a non-zero result column count, regardless of whether + the statement actually produces any result rows. + ================================================================== - .callback = a function which gets called for each row of the - FIRST statement in the SQL which has result _columns_, but only - if that statement has any result _rows_. The callback's "this" - is the options object. The second argument passed to the - callback is always the current Stmt object (so that the caller - may collect column names, or similar). The 2nd argument to the - callback is always the Stmt instance, as it's needed if the - caller wants to fetch the column names or some such. The first - argument passed to the callback defaults to the current Stmt - object but may be changed with ... + result set, but only if that statement has any result + _rows_. The callback's "this" is the options object. The second + argument passed to the callback is always the current Stmt + object (so that the caller may collect column names, or + similar). The 2nd argument to the callback is always the Stmt + instance, as it's needed if the caller wants to fetch the + column names or some such (noting that they could also be + fetched via `this.columnNames`, if the client provides the + `columnNames` option). - - .rowMode = may take one of several forms: + ACHTUNG: The callback MUST NOT modify the Stmt object. Calling + any of the Stmt.get() variants, Stmt.getColumnName(), or + similar, is legal, but calling step() or finalize() is + not. Routines which are illegal in this context will trigger an + exception. - A) If `rowMode` is an integer, only the single value from that - result column (0-based) will be passed on. + The first argument passed to the callback defaults to an array of + values from the current result row but may be changed with ... - B) A string describing what type of argument should be passed + - .rowMode = specifies the type of he callback's first argument. + It may be any of... + + A) A string describing what type of argument should be passed as the first argument to the callback: - B.1) 'array' (the default) causes the results of + A.1) 'array' (the default) causes the results of `stmt.get([])` to be passed to passed on and/or appended to `resultRows`. - B.2) 'object' causes the results of `stmt.get({})` to be - passed to the `callback` and/or appended to `resultRows`. + A.2) 'object' causes the results of + `stmt.get(Object.create(null))` to be passed to the + `callback` and/or appended to `resultRows`. Achtung: an SQL + result may have multiple columns with identical names. In + that case, the right-most column will be the one set in this + object! - B.3) 'stmt' causes the current Stmt to be passed to the + A.3) 'stmt' causes the current Stmt to be passed to the callback, but this mode will trigger an exception if `resultRows` is an array because appending the statement to the array would be unhelpful. + B) An integer, indicating a zero-based column in the result + row. Only that one single value will be passed on. + C) A string with a minimum length of 2 and leading character of ':', '$', or '@' will fetch the row as an object, extract that one field, and pass that field's value to the callback. Note that these keys are case-sensitive so must match the case used in the SQL. e.g. `"select a A from t"` with a `rowMode` of '$A' - would work but '$a' would not (it would result in `undefined`). + would work but '$a' would not. A reference to a column not in + the result set will trigger an exception on the first row (as + the check is not performed until rows are fetched). Any other `rowMode` value triggers an exception. - .resultRows: if this is an array, it functions similarly to - the `callback` option: each row of the result set (if any) of - the FIRST first statement which has result _columns_ is - appended to the array in the format specified for the `rowMode` - option, with the exception that the only legal values for - `rowMode` in this case are 'array', 'object', or an integer, - none of which are the default for `rowMode`. It is legal to use - both `resultRows` and `callback`, but `resultRows` is likely - much simpler to use for small data sets and can be used over a - WebWorker-style message interface. execMulti() throws if - `resultRows` is set and `rowMode` is 'stmt' (which is the - default!). + the `callback` option: each row of the result set (if any), + with the exception that the `rowMode` 'stmt' is not legal. It + is legal to use both `resultRows` and `callback`, but + `resultRows` is likely much simpler to use for small data sets + and can be used over a WebWorker-style message interface. + exec() throws if `resultRows` is set and `rowMode` is 'stmt'. - - 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. - - See also the exec() method, which is a close cousin of this - one. - - ACHTUNG #1: The callback MUST NOT modify the Stmt - object. Calling any of the Stmt.get() variants, - Stmt.getColumnName(), or similar, is legal, but calling - step() or finalize() is not. Routines which are illegal - in this context will trigger an exception. - - ACHTUNG #2: The semantics of the `bind` and `callback` - options may well change or those options may be removed - altogether for this function (but retained for exec()). - Generally speaking, neither bind parameters nor a callback - are generically useful when executing multi-statement SQL. + - .columnNames: if this is an array, the column names of the + result set are stored in this array before the callback (if + any) is triggered (regardless of whether the query produces any + result rows). If no statement has result columns, this value is + unchanged. Achtung: an SQL result may have multiple columns with + identical names. */ - execMulti: function(/*(sql [,obj]) || (obj)*/){ + exec: function(/*(sql [,obj]) || (obj)*/){ affirmDbOpen(this); const wasm = capi.wasm; - const arg = (BindTypes===arguments[2] - /* ^^^ Being passed on from exec() */ - ? arguments[0] : parseExecArgs(arguments)); - if(!arg.sql) return this; + const arg = parseExecArgs(arguments); + if(!arg.sql){ + return (''===arg.sql) ? this : toss3("exec() requires an SQL string."); + } const opt = arg.opt; const callback = opt.callback; - const resultRows = (Array.isArray(opt.resultRows) + let resultRows = (Array.isArray(opt.resultRows) ? opt.resultRows : undefined); - if(resultRows && 'stmt'===opt.rowMode){ - toss3("rowMode 'stmt' is not valid in combination", - "with a resultRows array."); - } let rowMode = (((callback||resultRows) && (undefined!==opt.rowMode)) ? opt.rowMode : undefined); let stmt; let bind = opt.bind; + let doneFirstQuery = false/*true once we handle a result-returning query*/; const stack = wasm.scopedAllocPush(); try{ const isTA = util.isSQLableTypedArray(arg.sql) @@ -604,8 +550,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ re-preparing an empty statement. */ ){ wasm.setMemValue(ppStmt, 0, wasm.ptrIR); wasm.setMemValue(pzTail, 0, wasm.ptrIR); - DB.checkRc(this, capi.sqlite3_prepare_v2( - this.pointer, pSql, sqlByteLen, ppStmt, pzTail + DB.checkRc(this, capi.sqlite3_prepare_v3( + this.pointer, pSql, sqlByteLen, 0, ppStmt, pzTail )); const pStmt = wasm.getMemValue(ppStmt, wasm.ptrIR); pSql = wasm.getMemValue(pzTail, wasm.ptrIR); @@ -619,17 +565,20 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ stmt.bind(bind); bind = null; } - if(stmt.columnCount && undefined!==rowMode){ + if(!doneFirstQuery && stmt.columnCount){ /* Only forward SELECT results for the FIRST query in the SQL which potentially has them. */ + doneFirstQuery = true; + if(Array.isArray(opt.columnNames)){ + stmt.getColumnNames(opt.columnNames); + } while(stmt.step()){ stmt._isLocked = true; const row = arg.cbArg(stmt); if(resultRows) resultRows.push(row); - if(callback) callback(row,stmt); + if(callback) callback.apply(opt,[row,stmt]); stmt._isLocked = false; } - rowMode = undefined; }else{ // Do we need to while(stmt.step()){} here? stmt.step(); @@ -637,10 +586,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ stmt.finalize(); stmt = null; } - }catch(e){ - console.warn("DB.execMulti() is propagating exception",opt,e); + }/*catch(e){ + console.warn("DB.exec() is propagating exception",opt,e); throw e; - }finally{ + }*/finally{ if(stmt){ delete stmt._isLocked; stmt.finalize(); @@ -648,7 +597,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ wasm.scopedAllocPop(stack); } return this; - }/*execMulti()*/, + }/*exec()*/, /** Creates a new scalar UDF (User-Defined Function) which is accessible via SQL code. This function may be called in any @@ -907,7 +856,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ this.exec("RELEASE oo1"); return rc; }catch(e){ - this.execMulti("ROLLBACK to SAVEPOINT oo1; RELEASE SAVEPOINT oo1"); + this.exec("ROLLBACK to SAVEPOINT oo1; RELEASE SAVEPOINT oo1"); throw e; } }, diff --git a/ext/wasm/api/sqlite3-api-worker1.js b/ext/wasm/api/sqlite3-api-worker1.js index f882f6d76a..6ca337df1d 100644 --- a/ext/wasm/api/sqlite3-api-worker1.js +++ b/ext/wasm/api/sqlite3-api-worker1.js @@ -589,7 +589,7 @@ sqlite3.initWorker1API = function(){ }; if(err.stack){ result.stack = ('string'===typeof err.stack) - ? err.stack.split('\n') : err.stack; + ? err.stack.split(/\n\s*/) : err.stack; } if(0) console.warn("Worker is propagating an exception to main thread.", "Reporting it _here_ for the stack trace:",err,result); diff --git a/ext/wasm/demo-oo1.js b/ext/wasm/demo-oo1.js index 333d3c204c..353ed50a64 100644 --- a/ext/wasm/demo-oo1.js +++ b/ext/wasm/demo-oo1.js @@ -144,23 +144,6 @@ }); log("Result column names:",columnNames); - /** - Main differences between exec() and execMulti(): - - - execMulti() traverses all statements in the input SQL - - - exec() supports a couple options not supported by execMulti(), - and vice versa. - - - execMulti() result callback/array only activates for the - first statement which has result columns. It is arguable - whether it should support a callback at all, and that - feature may be removed. - - - execMulti() column-bind data only activates for the first statement - with bindable columns. This feature is arguable and may be removed. - */ - if(0){ warn("UDF will throw because of incorrect arg count..."); db.exec("select twice(1,2,3)"); @@ -187,7 +170,7 @@ log("In savepoint: count(*) from t =",db.selectValue("select count(*) from t")); D.savepoint(function(DD){ const rows = []; - D.execMulti({ + D.exec({ sql: ["insert into t(a,b) values(99,100);", "select count(*) from t"], rowMode: 0, diff --git a/ext/wasm/testing2.js b/ext/wasm/testing2.js index 8d9e66dc1f..0a31c470a6 100644 --- a/ext/wasm/testing2.js +++ b/ext/wasm/testing2.js @@ -70,7 +70,7 @@ (ev.workerRespondTime - ev.workerReceivedTime),"ms.", "Round-trip event time =", (performance.now() - ev.departureTime),"ms.", - (evd.errorClass ? ev.message : ""), evd + (evd.errorClass ? ev.message : "")//, JSON.stringify(evd) ); }; @@ -149,10 +149,9 @@ throw new Error("This is not supposed to be reached."); }; runOneTest('exec',{ - sql: ["create table t(a,b)", + sql: ["create table t(a,b);", "insert into t(a,b) values(1,2),(3,4),(5,6)" - ].join(';'), - multi: true, + ], resultRows: [], columnNames: [] }, function(ev){ ev = ev.result; @@ -161,7 +160,7 @@ }); runOneTest('exec',{ sql: 'select a a, b b from t order by a', - resultRows: [], columnNames: [], + resultRows: [], columnNames: [], saveSql:[] }, function(ev){ ev = ev.result; T.assert(3===ev.resultRows.length) @@ -170,6 +169,7 @@ .assert(2===ev.columnNames.length) .assert('b'===ev.columnNames[1]); }); + //if(1){ error("Returning prematurely for testing."); return; } runOneTest('exec',{ sql: 'select a a, b b from t order by a', resultRows: [], columnNames: [], @@ -200,12 +200,12 @@ dbMsgHandler.resultRowTest1.counter = 0; }); runOneTest('exec',{ - multi: true, sql:[ - 'pragma foreign_keys=0;', + "pragma foreign_keys=0;", // ^^^ arbitrary query with no result columns - 'select a, b from t order by a desc; select a from t;' - // multi-exec only honors results from the first + "select a, b from t order by a desc;", + "select a from t;" + // multi-statement exec only honors results from the first // statement with result columns (regardless of whether) // it has any rows). ], diff --git a/manifest b/manifest index feef45cc58..3f6cea0347 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Refactor\sand\sexpand\sthe\sworker1\sdocs,\sconsolidating\sthem\sinto\sthe\stop\sof\stheir\sfile\sinstead\sof\sscattered\saround\sthe\sinternals.\sAccommodate\san\sAPI\schange\sfrom\syesterday\sin\sdemo-oo1.js. -D 2022-08-25T11:39:12.535 +C Consolidate\soo1.DB.exec()\sand\soo1.DB.execMulti()\sinto\soo1.DB.exec().\sThis\sis\sa\sbit\sless\sefficient\sbut\scertainly\seasier\sfor\sa\sclient\sto\sdeal\swith\sand\slightens\sthe\smaintenance\sburden. +D 2022-08-25T13:27:52.079 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -483,10 +483,10 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 1a12e64060c2cb0defd34656a76a9b1d7ed58459c290249bb31567c806fd44de F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3 -F ext/wasm/api/sqlite3-api-oo1.js a207d53bcc11955cc8f844aa59b30caaba8f57573222f62010628cfa4b1f4444 +F ext/wasm/api/sqlite3-api-oo1.js 329f07524e4a5d12960a0d17845443a4495b3e34b53b0182b1b944c626864974 F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db98a17685ab7a34a03953c7a -F ext/wasm/api/sqlite3-api-worker1.js 38db6c3d77798a0ef8be78ae6d421ef144e2e9602cdabdd7a93c7fcb7a2d449f +F ext/wasm/api/sqlite3-api-worker1.js 73579555563b789785ae83724014eaf31811073aad9be6596c8336ffb51edd71 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247 @@ -494,7 +494,7 @@ F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 F ext/wasm/common/whwasmutil.js 41b8e097e0a9cb07c24c0ede3c81b72470a63f4a4efb07f75586dc131569f5ae F ext/wasm/demo-oo1.html 75646855b38405d82781246fd08c852a2b3bee05dd9f0fe10ab655a8cffb79aa -F ext/wasm/demo-oo1.js 77b837b0fe13b542cee893c8caf84009482986bd43cf775197dfeb1e62ec0a2b +F ext/wasm/demo-oo1.js aad38cb90b6fa7fd4d1184e759b25056fb4ed45c4957c458896354281259515f F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 @@ -515,7 +515,7 @@ F ext/wasm/testing-worker1-promiser.js c62b5879339eef0b21aebd9d75bc125c86530edc1 F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409 F ext/wasm/testing1.js 2def7a86c52ff28b145cb86188d5c7a49d5993f9b78c50d140e1c31551220955 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 -F ext/wasm/testing2.js ab4ae24cd3ffe814370b35515aea426647a6f9d271c6542cf18e580470540615 +F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2009,8 +2009,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 1bb37e5c477b9eb098362f74a45a55be23d450fe45cdff58c1cbff08b5b3998f -R 317eb3fcfe686c9323e52473ace36982 +P 0a65747047322b7b585e281ac275e437ce3f46e1d06105c19117213929a906ad +R 14238254f8c2a9ecda72692d6551fb99 U stephan -Z 2516659ac708c3f3e5c0bf51001da2da +Z 0ceace5a4ad7c624466e4c9b471faa7f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 31227d768b..7080e62c57 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0a65747047322b7b585e281ac275e437ce3f46e1d06105c19117213929a906ad \ No newline at end of file +7eff7213dff553b76d7ce45063e3c4a19544716611a0b609593d704076b38d0b \ No newline at end of file From d7d1098baa959ac5c24f69fa57299f161891b59f Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 29 Aug 2022 08:04:55 +0000 Subject: [PATCH 031/428] oo #1 api: correct a case where a null callback is called. Rename some vars for clarity. Increase wasm-side memory in order to be able to load the speedtest1 output. FossilOrigin-Name: b5058f14fadbc8a1886f27cff08593dd2c8e2b2cb6d7bed3b8733a55f031989f --- ext/wasm/GNUmakefile | 1 + ext/wasm/api/sqlite3-api-oo1.js | 8 ++++---- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index db96a42ffc..40a69b200c 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -202,6 +202,7 @@ else endif emcc.jsflags += $(emcc.environment) #emcc.jsflags += -sINITIAL_MEMORY=13107200 +emcc.jsflags += -sINITIAL_MEMORY=64225280 #emcc.jsflags += -sTOTAL_STACK=4194304 emcc.jsflags += -sEXPORT_NAME=sqlite3InitModule emcc.jsflags += -sGLOBAL_BASE=4096 # HYPOTHETICALLY keep func table indexes from overlapping w/ heap addr. diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index c2b0601374..6317f97f51 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -521,7 +521,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ? opt.rowMode : undefined); let stmt; let bind = opt.bind; - let doneFirstQuery = false/*true once we handle a result-returning query*/; + let runFirstQuery = !!(arg.cbArg || opt.columnNames) /* true to evaluate the first result-returning query */; const stack = wasm.scopedAllocPush(); try{ const isTA = util.isSQLableTypedArray(arg.sql) @@ -565,14 +565,14 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ stmt.bind(bind); bind = null; } - if(!doneFirstQuery && stmt.columnCount){ + if(runFirstQuery && stmt.columnCount){ /* Only forward SELECT results for the FIRST query in the SQL which potentially has them. */ - doneFirstQuery = true; + runFirstQuery = false; if(Array.isArray(opt.columnNames)){ stmt.getColumnNames(opt.columnNames); } - while(stmt.step()){ + while(!!arg.cbArg && stmt.step()){ stmt._isLocked = true; const row = arg.cbArg(stmt); if(resultRows) resultRows.push(row); diff --git a/manifest b/manifest index 738a1b98a5..532fe66a0e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\schanges\sfrom\strunk\sinto\sthe\sfiddle-opfs\sbranch. -D 2022-08-25T19:38:54.407 +C oo\s#1\sapi:\scorrect\sa\scase\swhere\sa\snull\scallback\sis\scalled.\sRename\ssome\svars\sfor\sclarity.\sIncrease\swasm-side\smemory\sin\sorder\sto\sbe\sable\sto\sload\sthe\sspeedtest1\soutput. +D 2022-08-29T08:04:55.473 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 79f0ddba6b6b474fad46a927aa9cd0fdbeb372c5332c84e8096d6b490df0f9db +F ext/wasm/GNUmakefile 12b4cefa1653df172ac54400f83b626b4969b4007da2ce523ebc27c8726656b0 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -483,7 +483,7 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 1a12e64060c2cb0defd34656a76a9b1d7ed58459c290249bb31567c806fd44de F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3 -F ext/wasm/api/sqlite3-api-oo1.js 329f07524e4a5d12960a0d17845443a4495b3e34b53b0182b1b944c626864974 +F ext/wasm/api/sqlite3-api-oo1.js ae7b07445b47cd3b875a8f926abad6c1b836aaba0c9e16c090051f5c8461949d F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db98a17685ab7a34a03953c7a F ext/wasm/api/sqlite3-api-worker1.js 73579555563b789785ae83724014eaf31811073aad9be6596c8336ffb51edd71 @@ -2009,8 +2009,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 7eff7213dff553b76d7ce45063e3c4a19544716611a0b609593d704076b38d0b c72756b0f2db30c622f00f43be9245d50e36049bd7740ee6332164f0c48f9c3d -R 6cbaead5bd5c21e8bcf8c5222e15e555 +P 34279797be0922b9df05e0d4f327f8582ab25fb6fb21583f8441ddf5deded891 +R 33c519177a8614fb691ab50256f2cf44 U stephan -Z 2bf8ba3c4f232c47481eaff62acadb99 +Z d1dd6a72031bb9355624c4c89a613214 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ac06f3fffa..6044ded307 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -34279797be0922b9df05e0d4f327f8582ab25fb6fb21583f8441ddf5deded891 \ No newline at end of file +b5058f14fadbc8a1886f27cff08593dd2c8e2b2cb6d7bed3b8733a55f031989f \ No newline at end of file From 9a2efd8e1f488c3c00295bc7490e3b40ebc192c4 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 29 Aug 2022 09:51:17 +0000 Subject: [PATCH 032/428] Add begin/end comment markers to the test blocks in speedtest1 --script output to facilitate chopping up the output into chunks for JS-side processing. FossilOrigin-Name: ef0b7ef2d2e19b1f9437fdd7e24f040f662d9907d1fa17c6a3892fcf091b849e --- manifest | 12 ++++++------ manifest.uuid | 2 +- test/speedtest1.c | 10 ++++++++++ 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 532fe66a0e..1c823620f6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C oo\s#1\sapi:\scorrect\sa\scase\swhere\sa\snull\scallback\sis\scalled.\sRename\ssome\svars\sfor\sclarity.\sIncrease\swasm-side\smemory\sin\sorder\sto\sbe\sable\sto\sload\sthe\sspeedtest1\soutput. -D 2022-08-29T08:04:55.473 +C Add\sbegin/end\scomment\smarkers\sto\sthe\stest\sblocks\sin\sspeedtest1\s--script\soutput\sto\sfacilitate\schopping\sup\sthe\soutput\sinto\schunks\sfor\sJS-side\sprocessing. +D 2022-08-29T09:51:17.953 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1490,7 +1490,7 @@ F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715 F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d1f0c -F test/speedtest1.c 8bf7ebac9ac316feed6656951249db531dc380c73fb3e3b22e224ffda96beff6 +F test/speedtest1.c ad03c49d993b6fd4e8e6ad1df561edd1c17ec85226edd2137c355e404a0f985d F test/spellfix.test 951a6405d49d1a23d6b78027d3877b4a33eeb8221dcab5704b499755bb4f552e F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3 F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33 @@ -2009,8 +2009,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 34279797be0922b9df05e0d4f327f8582ab25fb6fb21583f8441ddf5deded891 -R 33c519177a8614fb691ab50256f2cf44 +P b5058f14fadbc8a1886f27cff08593dd2c8e2b2cb6d7bed3b8733a55f031989f +R 36f1b21cb1d4644e92fb6c673b070ffc U stephan -Z d1dd6a72031bb9355624c4c89a613214 +Z 7df3fe59906fdbadae6efee36992f3be # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6044ded307..aef69a98ac 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b5058f14fadbc8a1886f27cff08593dd2c8e2b2cb6d7bed3b8733a55f031989f \ No newline at end of file +ef0b7ef2d2e19b1f9437fdd7e24f040f662d9907d1fa17c6a3892fcf091b849e \ No newline at end of file diff --git a/test/speedtest1.c b/test/speedtest1.c index b115a57e2b..7f0e3d34a9 100644 --- a/test/speedtest1.c +++ b/test/speedtest1.c @@ -372,10 +372,12 @@ int speedtest1_numbername(unsigned int n, char *zOut, int nOut){ #define NAMEWIDTH 60 static const char zDots[] = "......................................................................."; +static int iTestNumber = 0; /* Current test # for begin/end_test(). */ void speedtest1_begin_test(int iTestNum, const char *zTestName, ...){ int n = (int)strlen(zTestName); char *zName; va_list ap; + iTestNumber = iTestNum; va_start(ap, zTestName); zName = sqlite3_vmprintf(zTestName, ap); va_end(ap); @@ -384,6 +386,9 @@ void speedtest1_begin_test(int iTestNum, const char *zTestName, ...){ zName[NAMEWIDTH] = 0; n = NAMEWIDTH; } + if( g.pScript ){ + fprintf(g.pScript,"-- begin test %d\n", iTestNumber); + } if( g.bSqlOnly ){ printf("/* %4d - %s%.*s */\n", iTestNum, zName, NAMEWIDTH-n, zDots); }else{ @@ -404,6 +409,10 @@ void speedtest1_exec(const char*,...); void speedtest1_end_test(void){ sqlite3_int64 iElapseTime = speedtest1_timestamp() - g.iStart; if( g.doCheckpoint ) speedtest1_exec("PRAGMA wal_checkpoint;"); + assert( iTestNumber > 0 ); + if( g.pScript ){ + fprintf(g.pScript,"-- end test %d\n", iTestNumber); + } if( !g.bSqlOnly ){ g.iTotal += iElapseTime; printf("%4d.%03ds\n", (int)(iElapseTime/1000), (int)(iElapseTime%1000)); @@ -412,6 +421,7 @@ void speedtest1_end_test(void){ sqlite3_finalize(g.pStmt); g.pStmt = 0; } + iTestNumber = 0; } /* Report end of testing */ From f2e624ea918d6509165a07205d08acd7ffe77927 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 29 Aug 2022 12:31:57 +0000 Subject: [PATCH 033/428] Add get/setPtrValue() to the common wasm utils. FossilOrigin-Name: 24d70fc458c6002d5ff3c9f8ce7c66bde299b32aca6417c2dd1236e1412b036d --- ext/wasm/api/sqlite3-api-oo1.js | 4 ++-- ext/wasm/common/whwasmutil.js | 9 +++++++++ manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 6317f97f51..5d4b19b5a9 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -553,8 +553,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ DB.checkRc(this, capi.sqlite3_prepare_v3( this.pointer, pSql, sqlByteLen, 0, ppStmt, pzTail )); - const pStmt = wasm.getMemValue(ppStmt, wasm.ptrIR); - pSql = wasm.getMemValue(pzTail, wasm.ptrIR); + const pStmt = wasm.getPtrValue(ppStmt); + pSql = wasm.getPtrValue(pzTail); sqlByteLen = pSqlEnd - pSql; if(!pStmt) continue; if(Array.isArray(opt.saveSql)){ diff --git a/ext/wasm/common/whwasmutil.js b/ext/wasm/common/whwasmutil.js index e51f690e29..3fef06f724 100644 --- a/ext/wasm/common/whwasmutil.js +++ b/ext/wasm/common/whwasmutil.js @@ -654,6 +654,15 @@ self.WhWasmUtilInstaller = function(target){ toss('Invalid type for setMemValue(): ' + type); }; + + /** Convenience form of getMemValue() intended for fetching + pointer-to-pointer values. */ + target.getPtrValue = (ptr)=>target.getMemValue(ptr, ptrIR); + + /** Convenience form of setMemValue() intended for setting + pointer-to-pointer values. */ + target.setPtrValue = (ptr, value)=>target.setMemValue(ptr, value, ptrIR); + /** Expects ptr to be a pointer into the WASM heap memory which refers to a NUL-terminated C-style string encoded as UTF-8. diff --git a/manifest b/manifest index 1c823620f6..4ae286c3d5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sbegin/end\scomment\smarkers\sto\sthe\stest\sblocks\sin\sspeedtest1\s--script\soutput\sto\sfacilitate\schopping\sup\sthe\soutput\sinto\schunks\sfor\sJS-side\sprocessing. -D 2022-08-29T09:51:17.953 +C Add\sget/setPtrValue()\sto\sthe\scommon\swasm\sutils. +D 2022-08-29T12:31:57.599 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -483,7 +483,7 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 1a12e64060c2cb0defd34656a76a9b1d7ed58459c290249bb31567c806fd44de F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3 -F ext/wasm/api/sqlite3-api-oo1.js ae7b07445b47cd3b875a8f926abad6c1b836aaba0c9e16c090051f5c8461949d +F ext/wasm/api/sqlite3-api-oo1.js 46a5151610076f45472c8d4fd31b728cc12e6fac4d44ea5c7cd1af087a906539 F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db98a17685ab7a34a03953c7a F ext/wasm/api/sqlite3-api-worker1.js 73579555563b789785ae83724014eaf31811073aad9be6596c8336ffb51edd71 @@ -492,7 +492,7 @@ F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa51 F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 -F ext/wasm/common/whwasmutil.js 41b8e097e0a9cb07c24c0ede3c81b72470a63f4a4efb07f75586dc131569f5ae +F ext/wasm/common/whwasmutil.js e1c6779c977c8614053e24311d489dd4872962cb58c27013d4778f0dd7329e9c F ext/wasm/demo-oo1.html 75646855b38405d82781246fd08c852a2b3bee05dd9f0fe10ab655a8cffb79aa F ext/wasm/demo-oo1.js aad38cb90b6fa7fd4d1184e759b25056fb4ed45c4957c458896354281259515f F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f @@ -2009,8 +2009,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P b5058f14fadbc8a1886f27cff08593dd2c8e2b2cb6d7bed3b8733a55f031989f -R 36f1b21cb1d4644e92fb6c673b070ffc +P ef0b7ef2d2e19b1f9437fdd7e24f040f662d9907d1fa17c6a3892fcf091b849e +R b442ab29e0d8d5bef6fe7f9423ce5596 U stephan -Z 7df3fe59906fdbadae6efee36992f3be +Z 5511f58972906137802924f203cb8dde # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index aef69a98ac..50e61bf33a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ef0b7ef2d2e19b1f9437fdd7e24f040f662d9907d1fa17c6a3892fcf091b849e \ No newline at end of file +24d70fc458c6002d5ff3c9f8ce7c66bde299b32aca6417c2dd1236e1412b036d \ No newline at end of file From 2cae138fbae5404fc8fc5319ba52abdb0d723c0c Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 29 Aug 2022 12:39:34 +0000 Subject: [PATCH 034/428] Add batch-runner.js for running batch SQL scripts with timing info. FossilOrigin-Name: 11f3ed61150c5940da6c157e5063e70c3aa0628dfd0023c47bb65b00af74ab1f --- ext/wasm/GNUmakefile | 18 ++- ext/wasm/batch-runner.html | 59 +++++++ ext/wasm/batch-runner.js | 237 ++++++++++++++++++++++++++++ ext/wasm/split-speedtest1-script.sh | 11 ++ manifest | 15 +- manifest.uuid | 2 +- 6 files changed, 333 insertions(+), 9 deletions(-) create mode 100644 ext/wasm/batch-runner.html create mode 100644 ext/wasm/batch-runner.js create mode 100755 ext/wasm/split-speedtest1-script.sh diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 40a69b200c..25d1e98084 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -194,15 +194,17 @@ ifneq (0,$(ENABLE_WASMFS)) emcc.jsflags += -pthread -sWASMFS -sPTHREAD_POOL_SIZE=2 emcc.cflags += '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' emcc.environment := $(emcc.environment),worker + emcc.jsflags += -sINITIAL_MEMORY=128450560 else emcc.jsflags += -sALLOW_MEMORY_GROWTH # emcc: warning: USE_PTHREADS + ALLOW_MEMORY_GROWTH may run non-wasm code # slowly, see https://github.com/WebAssembly/design/issues/1271 # [-Wpthreads-mem-growth] + emcc.jsflags += -sINITIAL_MEMORY=13107200 + #emcc.jsflags += -sINITIAL_MEMORY=64225280 + # ^^^^ 64MB is not enough for WASMFS/OPFS test runs using batch-runner.js endif emcc.jsflags += $(emcc.environment) -#emcc.jsflags += -sINITIAL_MEMORY=13107200 -emcc.jsflags += -sINITIAL_MEMORY=64225280 #emcc.jsflags += -sTOTAL_STACK=4194304 emcc.jsflags += -sEXPORT_NAME=sqlite3InitModule emcc.jsflags += -sGLOBAL_BASE=4096 # HYPOTHETICALLY keep func table indexes from overlapping w/ heap addr. @@ -285,6 +287,18 @@ all: $(sqlite3.js) # End main Emscripten-based module build ######################################################################## +######################################################################## +# Bits for use with batch-runner.js... +speedtest1 := ../../speedtest1 +$(speedtest1): + $(MAKE) -C ../.. speedtest1 +speedtest1.sql: $(speedtest1) + $(speedtest1) --script $@ +batch-sql.in := $(sort $(wildcard *.sql)) +batch-runner.list: $(batch-sql.in) $(MAKEFILE) speedtest1.sql + bash split-speedtest1-script.sh speedtest1.sql + ls -1 *.sql | sort > $@ +batch: batch-runner.list ######################################################################## # fiddle_remote is the remote destination for the fiddle app. It diff --git a/ext/wasm/batch-runner.html b/ext/wasm/batch-runner.html new file mode 100644 index 0000000000..f5031f8fe1 --- /dev/null +++ b/ext/wasm/batch-runner.html @@ -0,0 +1,59 @@ + + + + + + + + + sqlite3-api batch SQL runner + + +
sqlite3-api batch SQL runner
+ +
+
+
Initializing app...
+
+ 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. +
+
+
Downloading...
+
+ +
+

ACHTUNG: this file requires a generated input list + file. Run "make batch" from this directory to generate it. +

+

WARNING: if the WASMFS/OPFS layer crashes, this page may + become completely unresponsive and need to be closed and + reloaded to recover. +

+
+
+ + + +
+
+
+ + + + + + + diff --git a/ext/wasm/batch-runner.js b/ext/wasm/batch-runner.js new file mode 100644 index 0000000000..b412dc2ced --- /dev/null +++ b/ext/wasm/batch-runner.js @@ -0,0 +1,237 @@ +/* + 2022-08-29 + + 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 batch SQL running for sqlite3-api.js. This file must be run in + main JS thread and sqlite3.js must have been loaded before it. +*/ +'use strict'; +(function(){ + const T = self.SqliteTestUtil; + const toss = function(...args){throw new Error(args.join(' '))}; + const debug = console.debug.bind(console); + + const App = { + e: { + output: document.querySelector('#test-output'), + selSql: document.querySelector('#sql-select'), + btnRun: document.querySelector('#sql-run'), + btnClear: document.querySelector('#output-clear') + }, + log: console.log.bind(console), + warn: console.warn.bind(console), + cls: function(){this.e.output.innerHTML = ''}, + logHtml2: function(cssClass,...args){ + const ln = document.createElement('div'); + if(cssClass) ln.classList.add(cssClass); + ln.append(document.createTextNode(args.join(' '))); + this.e.output.append(ln); + }, + logHtml: function(...args){ + console.log(...args); + if(1) this.logHtml2('', ...args); + }, + logErr: function(...args){ + console.error(...args); + if(1) this.logHtml2('error', ...args); + }, + + openDb: function(fn){ + if(this.pDb){ + toss("Already have an opened db."); + } + const capi = this.sqlite3.capi, wasm = capi.wasm; + const stack = wasm.scopedAllocPush(); + let pDb = 0; + try{ + const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; + const ppDb = wasm.scopedAllocPtr(); + const rc = capi.sqlite3_open_v2(fn, ppDb, oFlags, null); + pDb = wasm.getPtrValue(ppDb) + }finally{ + wasm.scopedAllocPop(stack); + } + this.logHtml("Opened db:",capi.sqlite3_db_filename(pDb, 'main')); + return this.pDb = pDb; + }, + + closeDb: function(){ + if(this.pDb){ + this.sqlite3.capi.sqlite3_close_v2(this.pDb); + this.pDb = undefined; + } + }, + + loadSqlList: async function(){ + const sel = this.e.selSql; + sel.innerHTML = ''; + this.blockControls(true); + const infile = 'batch-runner.list'; + this.logHtml("Loading list of SQL files:", infile); + let txt; + try{ + const r = await fetch(infile); + if(404 === r.status){ + toss("Missing file '"+infile+"'."); + } + if(!r.ok) toss("Loading",infile,"failed:",r.statusText); + txt = await r.text(); + }catch(e){ + this.logErr(e.message); + throw e; + }finally{ + this.blockControls(false); + } + const list = txt.split('\n'); + let opt; + if(0){ + opt = document.createElement('option'); + opt.innerText = "Select file to evaluate..."; + opt.value = ''; + opt.disabled = true; + opt.selected = true; + sel.appendChild(opt); + } + list.forEach(function(fn){ + opt = document.createElement('option'); + opt.value = opt.innerText = fn; + sel.appendChild(opt); + }); + this.logHtml("Loaded",infile); + }, + + /** Fetch ./fn and return its contents as a Uint8Array. */ + fetchFile: async function(fn){ + this.logHtml("Fetching",fn,"..."); + let sql; + try { + const r = await fetch(fn); + if(!r.ok) toss("Fetch failed:",r.statusText); + sql = new Uint8Array(await r.arrayBuffer()); + }catch(e){ + this.logErr(e.message); + throw e; + } + this.logHtml("Fetched",sql.length,"bytes from",fn); + return sql; + }, + + checkRc: function(rc){ + if(rc){ + toss("Prepare failed:",this.sqlite3.capi.sqlite3_errmsg(this.pDb)); + } + }, + + blockControls: function(block){ + [ + this.e.selSql, this.e.btnRun, this.e.btnClear + ].forEach((e)=>e.disabled = block); + }, + + /** Fetch ./fn and eval it as an SQL blob. */ + evalFile: async function(fn){ + const sql = await this.fetchFile(fn); + this.logHtml("Running",fn,'...'); + const capi = this.sqlite3.capi, wasm = capi.wasm; + let pStmt = 0, pSqlBegin; + const stack = wasm.scopedAllocPush(); + const metrics = Object.create(null); + metrics.prepTotal = metrics.stepTotal = 0; + metrics.stmtCount = 0; + this.blockControls(true); + // Use setTimeout() so that the above log messages run before the loop starts. + setTimeout((function(){ + metrics.timeStart = performance.now(); + try { + let t; + let sqlByteLen = sql.byteLength; + const [ppStmt, pzTail] = wasm.scopedAllocPtr(2); + pSqlBegin = wasm.alloc( sqlByteLen + 1/*SQL + NUL*/); + let pSql = pSqlBegin; + const pSqlEnd = pSqlBegin + sqlByteLen; + wasm.heap8().set(sql, pSql); + wasm.setMemValue(pSql + sqlByteLen, 0); + while(wasm.getMemValue(pSql,'i8')){ + pStmt = 0; + wasm.setPtrValue(ppStmt, 0); + wasm.setPtrValue(pzTail, 0); + t = performance.now(); + let rc = capi.sqlite3_prepare_v3( + this.pDb, pSql, sqlByteLen, 0, ppStmt, pzTail + ); + metrics.prepTotal += performance.now() - t; + this.checkRc(rc); + ++metrics.stmtCount; + pStmt = wasm.getPtrValue(ppStmt); + pSql = wasm.getPtrValue(pzTail); + sqlByteLen = pSqlEnd - pSql; + if(!pStmt) continue/*empty statement*/; + t = performance.now(); + rc = capi.sqlite3_step(pStmt); + metrics.stepTotal += performance.now() - t; + switch(rc){ + case capi.SQLITE_ROW: + case capi.SQLITE_DONE: break; + default: this.checkRc(rc); toss("Not reached."); + } + } + }catch(e){ + this.logErr(e.message); + throw e; + }finally{ + wasm.dealloc(pSqlBegin); + wasm.scopedAllocPop(stack); + this.blockControls(false); + } + metrics.timeEnd = performance.now(); + metrics.timeTotal = (metrics.timeEnd - metrics.timeStart); + this.logHtml("Metrics:");//,JSON.stringify(metrics, undefined, ' ')); + this.logHtml("prepare() count:",metrics.stmtCount); + this.logHtml("Time in prepare_v2():",metrics.prepTotal,"ms", + "("+(metrics.prepTotal / metrics.stmtCount),"ms per prepare())"); + this.logHtml("Time in step():",metrics.stepTotal,"ms", + "("+(metrics.stepTotal / metrics.stmtCount),"ms per step())"); + this.logHtml("Total runtime:",metrics.timeTotal,"ms"); + this.logHtml("Overhead (time - prep - step):", + (metrics.timeTotal - metrics.prepTotal - metrics.stepTotal)+"ms"); + }.bind(this)), 10); + }, + + run: function(sqlite3){ + this.sqlite3 = sqlite3; + const capi = sqlite3.capi, wasm = capi.wasm; + this.logHtml("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); + this.logHtml("WASM heap size =",wasm.heap8().length); + this.logHtml("WARNING: if the WASMFS/OPFS layer crashes, this page may", + "become unresponsive and need to be closed and ", + "reloaded to recover."); + const pDir = capi.sqlite3_web_persistent_dir(); + const dbFile = pDir ? pDir+"/speedtest.db" : ":memory:"; + if(pDir){ + // We initially need a clean db file, so... + capi.sqlite3_wasm_vfs_unlink(dbFile); + } + this.openDb(dbFile); + this.loadSqlList(); + const who = this; + this.e.btnClear.addEventListener('click', ()=>this.cls(), false); + this.e.btnRun.addEventListener('click', function(){ + if(!who.e.selSql.value) return; + who.evalFile(who.e.selSql.value); + }, false); + } + }/*App*/; + + self.sqlite3TestModule.initSqlite3().then(function(theEmccModule){ + self._MODULE = theEmccModule /* this is only to facilitate testing from the console */; + App.run(theEmccModule.sqlite3); + }); +})(); diff --git a/ext/wasm/split-speedtest1-script.sh b/ext/wasm/split-speedtest1-script.sh new file mode 100755 index 0000000000..c7dae3eb68 --- /dev/null +++ b/ext/wasm/split-speedtest1-script.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# Expects $1 to be a (speedtest1 --script) output file. Output is a +# series of SQL files extracted from that file. +infile=${1:?arg = speedtest1 --script output file} +testnums=$(grep -e '^-- begin test' "$infile" | cut -d' ' -f4) +#echo testnums=$testnums +for n in $testnums; do + ofile=$(printf "speedtest1-%03d.sql" $n) + sed -n -e "/^-- begin test $n\$/,/^-- end test $n\$/p" $infile > $ofile + echo -e "$n\t$ofile" +done diff --git a/manifest b/manifest index 4ae286c3d5..ca4d7ea9d8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sget/setPtrValue()\sto\sthe\scommon\swasm\sutils. -D 2022-08-29T12:31:57.599 +C Add\sbatch-runner.js\sfor\srunning\sbatch\sSQL\sscripts\swith\stiming\sinfo. +D 2022-08-29T12:39:34.874 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 12b4cefa1653df172ac54400f83b626b4969b4007da2ce523ebc27c8726656b0 +F ext/wasm/GNUmakefile 61c0d9dcda9ca174907f87770c9440b26408dc415d2e11a3cbcae017358fd08c F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -489,6 +489,8 @@ F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db F ext/wasm/api/sqlite3-api-worker1.js 73579555563b789785ae83724014eaf31811073aad9be6596c8336ffb51edd71 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 +F ext/wasm/batch-runner.html f9068c4b4222d0c11ba0590391892e4d7576ef1a4fb76974ba0bd3b80c6217f9 +F ext/wasm/batch-runner.js 57f325e40812a89f8d47c918963f278d7a249d158cf0d245d75e74577af638c8 F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 @@ -508,6 +510,7 @@ F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb43 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0 +F ext/wasm/split-speedtest1-script.sh dc187a66b692e21343a84767627e9e408dde504d9e92d4dfa63a2b43cedab292 x F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 @@ -2009,8 +2012,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P ef0b7ef2d2e19b1f9437fdd7e24f040f662d9907d1fa17c6a3892fcf091b849e -R b442ab29e0d8d5bef6fe7f9423ce5596 +P 24d70fc458c6002d5ff3c9f8ce7c66bde299b32aca6417c2dd1236e1412b036d +R ed1761d31b9a32171fc27afe1d89c1b7 U stephan -Z 5511f58972906137802924f203cb8dde +Z 4abbcdb5df49f5d67360636a18d94c2b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 50e61bf33a..36a59ff465 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -24d70fc458c6002d5ff3c9f8ce7c66bde299b32aca6417c2dd1236e1412b036d \ No newline at end of file +11f3ed61150c5940da6c157e5063e70c3aa0628dfd0023c47bb65b00af74ab1f \ No newline at end of file From 49a785fa34753687c00f3a492700efbdce0535aa Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 29 Aug 2022 17:41:16 +0000 Subject: [PATCH 035/428] Add test descriptions to the speedtest1 '-- begin' markers for potential display by the downstream JS code which uses those markers. FossilOrigin-Name: e5b7006f0f57f10a490d7eaeb7df77251a2f684602fed8ff161d8ce60033e7bc --- ext/wasm/split-speedtest1-script.sh | 6 +++++- manifest | 14 +++++++------- manifest.uuid | 2 +- test/speedtest1.c | 4 +++- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/ext/wasm/split-speedtest1-script.sh b/ext/wasm/split-speedtest1-script.sh index c7dae3eb68..e228a872ec 100755 --- a/ext/wasm/split-speedtest1-script.sh +++ b/ext/wasm/split-speedtest1-script.sh @@ -3,9 +3,13 @@ # series of SQL files extracted from that file. infile=${1:?arg = speedtest1 --script output file} testnums=$(grep -e '^-- begin test' "$infile" | cut -d' ' -f4) +if [ x = "x${testnums}" ]; then + echo "Could not parse any begin/end blocks out of $infile" 1>&2 + exit 1 +fi #echo testnums=$testnums for n in $testnums; do ofile=$(printf "speedtest1-%03d.sql" $n) - sed -n -e "/^-- begin test $n\$/,/^-- end test $n\$/p" $infile > $ofile + sed -n -e "/^-- begin test $n /,/^-- end test $n\$/p" $infile > $ofile echo -e "$n\t$ofile" done diff --git a/manifest b/manifest index ca4d7ea9d8..6eb43c0ef2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sbatch-runner.js\sfor\srunning\sbatch\sSQL\sscripts\swith\stiming\sinfo. -D 2022-08-29T12:39:34.874 +C Add\stest\sdescriptions\sto\sthe\sspeedtest1\s'--\sbegin'\smarkers\sfor\spotential\sdisplay\sby\sthe\sdownstream\sJS\scode\swhich\suses\sthose\smarkers. +D 2022-08-29T17:41:16.571 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -510,7 +510,7 @@ F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb43 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0 -F ext/wasm/split-speedtest1-script.sh dc187a66b692e21343a84767627e9e408dde504d9e92d4dfa63a2b43cedab292 x +F ext/wasm/split-speedtest1-script.sh 7138e474122686fecb274d78050f9bee6525bc64b72ff1af39f6fc0753ccfc1f x F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 @@ -1493,7 +1493,7 @@ F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715 F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d1f0c -F test/speedtest1.c ad03c49d993b6fd4e8e6ad1df561edd1c17ec85226edd2137c355e404a0f985d +F test/speedtest1.c 995c78f884e2388106b2b42de9e5a527e7cac01384b8f317ca0cc7b0814c9a18 F test/spellfix.test 951a6405d49d1a23d6b78027d3877b4a33eeb8221dcab5704b499755bb4f552e F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3 F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33 @@ -2012,8 +2012,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 24d70fc458c6002d5ff3c9f8ce7c66bde299b32aca6417c2dd1236e1412b036d -R ed1761d31b9a32171fc27afe1d89c1b7 +P 11f3ed61150c5940da6c157e5063e70c3aa0628dfd0023c47bb65b00af74ab1f +R 2ae8456ec9776838052861c3fb138f8f U stephan -Z 4abbcdb5df49f5d67360636a18d94c2b +Z 47d6dbc110cfca896ac7235ddcb7f7bf # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 36a59ff465..99bc257577 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -11f3ed61150c5940da6c157e5063e70c3aa0628dfd0023c47bb65b00af74ab1f \ No newline at end of file +e5b7006f0f57f10a490d7eaeb7df77251a2f684602fed8ff161d8ce60033e7bc \ No newline at end of file diff --git a/test/speedtest1.c b/test/speedtest1.c index 7f0e3d34a9..401217f105 100644 --- a/test/speedtest1.c +++ b/test/speedtest1.c @@ -387,7 +387,9 @@ void speedtest1_begin_test(int iTestNum, const char *zTestName, ...){ n = NAMEWIDTH; } if( g.pScript ){ - fprintf(g.pScript,"-- begin test %d\n", iTestNumber); + fprintf(g.pScript,"-- begin test %d %.*s\n", iTestNumber, n, zName) + /* maintenance reminder: ^^^ code in ext/wasm expects %d to be + ** field #4 (as in: cut -d' ' -f4). */; } if( g.bSqlOnly ){ printf("/* %4d - %s%.*s */\n", iTestNum, zName, NAMEWIDTH-n, zDots); From 2f06bf2541f651be4f0fdcaf2329f25b4eb2f3e5 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 29 Aug 2022 18:58:38 +0000 Subject: [PATCH 036/428] Lots of tweaking in batch-runner.js. Minor internal API update in OO #1 API. FossilOrigin-Name: 24b82b9504db3d8e1335c2300b133f897dc1a541026dc24be5b0ffd8be66d977 --- ext/wasm/GNUmakefile | 8 +- ext/wasm/api/sqlite3-api-oo1.js | 6 +- ext/wasm/batch-runner.html | 23 ++++- ext/wasm/batch-runner.js | 157 ++++++++++++++++++++++---------- manifest | 18 ++-- manifest.uuid | 2 +- 6 files changed, 147 insertions(+), 67 deletions(-) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 25d1e98084..6a0835ece4 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -294,10 +294,12 @@ $(speedtest1): $(MAKE) -C ../.. speedtest1 speedtest1.sql: $(speedtest1) $(speedtest1) --script $@ -batch-sql.in := $(sort $(wildcard *.sql)) -batch-runner.list: $(batch-sql.in) $(MAKEFILE) speedtest1.sql +speedtest1-000.sql: + echo "select 1;" > $@ +batch-runner.list: $(MAKEFILE) speedtest1.sql speedtest1-000.sql bash split-speedtest1-script.sh speedtest1.sql - ls -1 *.sql | sort > $@ + ls -1 *.sql | grep -v speedtest1.sql | sort > $@ +CLEAN_FILES += batch-runner.list speedtest1*.sql batch: batch-runner.list ######################################################################## diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 5d4b19b5a9..2cfb478865 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -540,7 +540,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ if(isTA) wasm.heap8().set(arg.sql, pSql); else wasm.jstrcpy(arg.sql, wasm.heap8(), pSql, sqlByteLen, false); wasm.setMemValue(pSql + sqlByteLen, 0/*NUL terminator*/); - while(wasm.getMemValue(pSql, 'i8') + while(pSql && wasm.getMemValue(pSql, 'i8') /* Maintenance reminder:^^^ _must_ be 'i8' or else we will very likely cause an endless loop. What that's doing is checking for a terminating NUL byte. If we @@ -548,8 +548,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ around the NUL terminator, and get stuck in and endless loop at the end of the SQL, endlessly re-preparing an empty statement. */ ){ - wasm.setMemValue(ppStmt, 0, wasm.ptrIR); - wasm.setMemValue(pzTail, 0, wasm.ptrIR); + wasm.setPtrValue(ppStmt, 0); + wasm.setPtrValue(pzTail, 0); DB.checkRc(this, capi.sqlite3_prepare_v3( this.pointer, pSql, sqlByteLen, 0, ppStmt, pzTail )); diff --git a/ext/wasm/batch-runner.html b/ext/wasm/batch-runner.html index f5031f8fe1..c91e591553 100644 --- a/ext/wasm/batch-runner.html +++ b/ext/wasm/batch-runner.html @@ -24,35 +24,48 @@
-

ACHTUNG: this file requires a generated input list +

+ This page is for running extracts from the output of speedtest --script. +

+

ACHTUNG: this file requires a generated input list file. Run "make batch" from this directory to generate it.

-

WARNING: if the WASMFS/OPFS layer crashes, this page may +

WARNING: if the WASMFS/OPFS layer crashes, this page may become completely unresponsive and need to be closed and reloaded to recover.


- - + + + +

+
(Log output is in reverse order, newest first!)
diff --git a/ext/wasm/batch-runner.js b/ext/wasm/batch-runner.js index b412dc2ced..0f5406f902 100644 --- a/ext/wasm/batch-runner.js +++ b/ext/wasm/batch-runner.js @@ -17,15 +17,19 @@ (function(){ const T = self.SqliteTestUtil; const toss = function(...args){throw new Error(args.join(' '))}; - const debug = console.debug.bind(console); + const warn = console.warn.bind(console); const App = { e: { output: document.querySelector('#test-output'), selSql: document.querySelector('#sql-select'), btnRun: document.querySelector('#sql-run'), - btnClear: document.querySelector('#output-clear') + btnRunNext: document.querySelector('#sql-run-next'), + btnRunRemaining: document.querySelector('#sql-run-remaining'), + btnClear: document.querySelector('#output-clear'), + btnReset: document.querySelector('#db-reset') }, + cache:{}, log: console.log.bind(console), warn: console.warn.bind(console), cls: function(){this.e.output.innerHTML = ''}, @@ -34,6 +38,7 @@ if(cssClass) ln.classList.add(cssClass); ln.append(document.createTextNode(args.join(' '))); this.e.output.append(ln); + //this.e.output.lastElementChild.scrollIntoViewIfNeeded(); }, logHtml: function(...args){ console.log(...args); @@ -44,29 +49,38 @@ if(1) this.logHtml2('error', ...args); }, - openDb: function(fn){ - if(this.pDb){ + openDb: function(fn, unlinkFirst=true){ + if(this.db && this.db.ptr){ toss("Already have an opened db."); } const capi = this.sqlite3.capi, wasm = capi.wasm; const stack = wasm.scopedAllocPush(); let pDb = 0; try{ + /*if(unlinkFirst && fn && ':memory:'!==fn){ + capi.sqlite3_wasm_vfs_unlink(fn); + }*/ const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; const ppDb = wasm.scopedAllocPtr(); const rc = capi.sqlite3_open_v2(fn, ppDb, oFlags, null); + if(rc) toss("sqlite3_open_v2() failed with code",rc); pDb = wasm.getPtrValue(ppDb) }finally{ wasm.scopedAllocPop(stack); } - this.logHtml("Opened db:",capi.sqlite3_db_filename(pDb, 'main')); - return this.pDb = pDb; + this.db = Object.create(null); + this.db.filename = fn; + this.db.ptr = pDb; + this.logHtml("Opened db:",fn); + return this.db.ptr; }, - closeDb: function(){ - if(this.pDb){ - this.sqlite3.capi.sqlite3_close_v2(this.pDb); - this.pDb = undefined; + closeDb: function(unlink=false){ + if(this.db && this.db.ptr){ + this.sqlite3.capi.sqlite3_close_v2(this.db.ptr); + this.logHtml("Closed db",this.db.filename); + if(unlink) capi.sqlite3_wasm_vfs_unlink(this.db.filename); + this.db.ptr = this.db.filename = undefined; } }, @@ -84,13 +98,15 @@ } if(!r.ok) toss("Loading",infile,"failed:",r.statusText); txt = await r.text(); + const warning = document.querySelector('#warn-list'); + if(warning) warning.remove(); }catch(e){ this.logErr(e.message); throw e; }finally{ this.blockControls(false); } - const list = txt.split('\n'); + const list = txt.split(/\n+/); let opt; if(0){ opt = document.createElement('option'); @@ -101,6 +117,7 @@ sel.appendChild(opt); } list.forEach(function(fn){ + if(!fn) return; opt = document.createElement('option'); opt.value = opt.innerText = fn; sel.appendChild(opt); @@ -109,7 +126,8 @@ }, /** Fetch ./fn and return its contents as a Uint8Array. */ - fetchFile: async function(fn){ + fetchFile: async function(fn, cacheIt=false){ + if(cacheIt && this.cache[fn]) return this.cache[fn]; this.logHtml("Fetching",fn,"..."); let sql; try { @@ -121,25 +139,28 @@ throw e; } this.logHtml("Fetched",sql.length,"bytes from",fn); + if(cacheIt) this.cache[fn] = sql; return sql; - }, + }/*fetchFile()*/, + /** Throws if the given sqlite3 result code is not 0. */ checkRc: function(rc){ - if(rc){ - toss("Prepare failed:",this.sqlite3.capi.sqlite3_errmsg(this.pDb)); + if(this.db.ptr && rc){ + toss("Prepare failed:",this.sqlite3.capi.sqlite3_errmsg(this.db.ptr)); } }, - blockControls: function(block){ - [ - this.e.selSql, this.e.btnRun, this.e.btnClear - ].forEach((e)=>e.disabled = block); + /** Disable or enable certain UI controls. */ + blockControls: function(disable){ + document.querySelectorAll('.disable-during-eval').forEach((e)=>e.disabled = disable); }, - + /** Fetch ./fn and eval it as an SQL blob. */ evalFile: async function(fn){ const sql = await this.fetchFile(fn); - this.logHtml("Running",fn,'...'); + const banner = "========================================"; + this.logHtml(banner, + "Running",fn,'('+sql.length,'bytes)...'); const capi = this.sqlite3.capi, wasm = capi.wasm; let pStmt = 0, pSqlBegin; const stack = wasm.scopedAllocPush(); @@ -147,35 +168,41 @@ metrics.prepTotal = metrics.stepTotal = 0; metrics.stmtCount = 0; this.blockControls(true); - // Use setTimeout() so that the above log messages run before the loop starts. - setTimeout((function(){ - metrics.timeStart = performance.now(); + if(this.gotErr){ + this.logErr("Cannot run ["+fn+"]: error cleanup is pending."); + return; + } + // Run this async so that the UI can be updated for the above header... + const ff = function(resolve, reject){ + metrics.evalFileStart = performance.now(); try { let t; let sqlByteLen = sql.byteLength; const [ppStmt, pzTail] = wasm.scopedAllocPtr(2); - pSqlBegin = wasm.alloc( sqlByteLen + 1/*SQL + NUL*/); + pSqlBegin = wasm.alloc( sqlByteLen + 1/*SQL + NUL*/) || toss("alloc(",sqlByteLen,") failed"); let pSql = pSqlBegin; const pSqlEnd = pSqlBegin + sqlByteLen; wasm.heap8().set(sql, pSql); wasm.setMemValue(pSql + sqlByteLen, 0); - while(wasm.getMemValue(pSql,'i8')){ - pStmt = 0; + let breaker = 0; + while(pSql && wasm.getMemValue(pSql,'i8')){ wasm.setPtrValue(ppStmt, 0); wasm.setPtrValue(pzTail, 0); t = performance.now(); let rc = capi.sqlite3_prepare_v3( - this.pDb, pSql, sqlByteLen, 0, ppStmt, pzTail + this.db.ptr, pSql, sqlByteLen, 0, ppStmt, pzTail ); metrics.prepTotal += performance.now() - t; this.checkRc(rc); - ++metrics.stmtCount; pStmt = wasm.getPtrValue(ppStmt); pSql = wasm.getPtrValue(pzTail); sqlByteLen = pSqlEnd - pSql; if(!pStmt) continue/*empty statement*/; + ++metrics.stmtCount; t = performance.now(); rc = capi.sqlite3_step(pStmt); + capi.sqlite3_finalize(pStmt); + pStmt = 0; metrics.stepTotal += performance.now() - t; switch(rc){ case capi.SQLITE_ROW: @@ -184,50 +211,88 @@ } } }catch(e){ - this.logErr(e.message); - throw e; + if(pStmt) capi.sqlite3_finalize(pStmt); + this.gotErr = e; + //throw e; + reject(e); + return; }finally{ wasm.dealloc(pSqlBegin); wasm.scopedAllocPop(stack); this.blockControls(false); } - metrics.timeEnd = performance.now(); - metrics.timeTotal = (metrics.timeEnd - metrics.timeStart); + metrics.evalFileEnd = performance.now(); + metrics.evalTimeTotal = (metrics.evalFileEnd - metrics.evalFileStart); this.logHtml("Metrics:");//,JSON.stringify(metrics, undefined, ' ')); this.logHtml("prepare() count:",metrics.stmtCount); this.logHtml("Time in prepare_v2():",metrics.prepTotal,"ms", "("+(metrics.prepTotal / metrics.stmtCount),"ms per prepare())"); this.logHtml("Time in step():",metrics.stepTotal,"ms", "("+(metrics.stepTotal / metrics.stmtCount),"ms per step())"); - this.logHtml("Total runtime:",metrics.timeTotal,"ms"); + this.logHtml("Total runtime:",metrics.evalTimeTotal,"ms"); this.logHtml("Overhead (time - prep - step):", - (metrics.timeTotal - metrics.prepTotal - metrics.stepTotal)+"ms"); - }.bind(this)), 10); - }, + (metrics.evalTimeTotal - metrics.prepTotal - metrics.stepTotal)+"ms"); + this.logHtml(banner,"End of",fn); + resolve(this); + }.bind(this); + let p; + if(1){ + p = new Promise(function(res,rej){ + setTimeout(()=>ff(res, rej), 50)/*give UI a chance to output the "running" banner*/; + }); + }else{ + p = new Promise(ff); + } + return p.catch((e)=>this.logErr("Error via evalFile("+fn+"):",e.message)); + }/*evalFile()*/, run: function(sqlite3){ + delete this.run; this.sqlite3 = sqlite3; const capi = sqlite3.capi, wasm = capi.wasm; this.logHtml("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); this.logHtml("WASM heap size =",wasm.heap8().length); - this.logHtml("WARNING: if the WASMFS/OPFS layer crashes, this page may", - "become unresponsive and need to be closed and ", - "reloaded to recover."); + this.loadSqlList(); const pDir = capi.sqlite3_web_persistent_dir(); const dbFile = pDir ? pDir+"/speedtest.db" : ":memory:"; - if(pDir){ - // We initially need a clean db file, so... - capi.sqlite3_wasm_vfs_unlink(dbFile); + if(!pDir){ + document.querySelector('#warn-opfs').remove(); } - this.openDb(dbFile); - this.loadSqlList(); + this.openDb(dbFile, !!pDir); const who = this; this.e.btnClear.addEventListener('click', ()=>this.cls(), false); this.e.btnRun.addEventListener('click', function(){ if(!who.e.selSql.value) return; who.evalFile(who.e.selSql.value); }, false); - } + this.e.btnRunNext.addEventListener('click', function(){ + ++who.e.selSql.selectedIndex; + if(!who.e.selSql.value) return; + who.evalFile(who.e.selSql.value); + }, false); + this.e.btnReset.addEventListener('click', function(){ + const fn = who.db.filename; + if(fn){ + who.closeDb(true); + who.openDb(fn,true); + } + }, false); + this.e.btnRunRemaining.addEventListener('click', async function(){ + let v = who.e.selSql.value; + const timeStart = performance.now(); + while(v){ + await who.evalFile(v); + if(who.gotError){ + who.logErr("Error handling script",v,":",who.gotError.message); + break; + } + ++who.e.selSql.selectedIndex; + v = who.e.selSql.value; + } + const timeTotal = performance.now() - timeStart; + who.logHtml("Run-remaining time:",timeTotal,"ms ("+(timeTotal/1000/60)+" minute(s))"); + }, false); + }/*run()*/ }/*App*/; self.sqlite3TestModule.initSqlite3().then(function(theEmccModule){ diff --git a/manifest b/manifest index 6eb43c0ef2..bfcc12ac8f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\stest\sdescriptions\sto\sthe\sspeedtest1\s'--\sbegin'\smarkers\sfor\spotential\sdisplay\sby\sthe\sdownstream\sJS\scode\swhich\suses\sthose\smarkers. -D 2022-08-29T17:41:16.571 +C Lots\sof\stweaking\sin\sbatch-runner.js.\sMinor\sinternal\sAPI\supdate\sin\sOO\s#1\sAPI. +D 2022-08-29T18:58:38.025 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 61c0d9dcda9ca174907f87770c9440b26408dc415d2e11a3cbcae017358fd08c +F ext/wasm/GNUmakefile cabfbb177d16c550442313416698e66dde575df27177d4ca3ccece5a66b37de4 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -483,14 +483,14 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 1a12e64060c2cb0defd34656a76a9b1d7ed58459c290249bb31567c806fd44de F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3 -F ext/wasm/api/sqlite3-api-oo1.js 46a5151610076f45472c8d4fd31b728cc12e6fac4d44ea5c7cd1af087a906539 +F ext/wasm/api/sqlite3-api-oo1.js 183e863eedaba547ffe4981ab95797813b60da46749992bca400d2646e5ccd82 F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db98a17685ab7a34a03953c7a F ext/wasm/api/sqlite3-api-worker1.js 73579555563b789785ae83724014eaf31811073aad9be6596c8336ffb51edd71 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 -F ext/wasm/batch-runner.html f9068c4b4222d0c11ba0590391892e4d7576ef1a4fb76974ba0bd3b80c6217f9 -F ext/wasm/batch-runner.js 57f325e40812a89f8d47c918963f278d7a249d158cf0d245d75e74577af638c8 +F ext/wasm/batch-runner.html e5c3edd4a6c9359f6d9e6c99cb5f87f09007d98fa1c705ed3efa370abcd4323e +F ext/wasm/batch-runner.js 84a465acde760de81d0372415cce56737799876395a878c44a7d3ce5dfe29e39 F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 @@ -2012,8 +2012,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 11f3ed61150c5940da6c157e5063e70c3aa0628dfd0023c47bb65b00af74ab1f -R 2ae8456ec9776838052861c3fb138f8f +P e5b7006f0f57f10a490d7eaeb7df77251a2f684602fed8ff161d8ce60033e7bc +R ac09921b66efbf8cc7311954674841d9 U stephan -Z 47d6dbc110cfca896ac7235ddcb7f7bf +Z 16cfbad53a69907d89a4919845ccdd35 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 99bc257577..622070996d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e5b7006f0f57f10a490d7eaeb7df77251a2f684602fed8ff161d8ce60033e7bc \ No newline at end of file +24b82b9504db3d8e1335c2300b133f897dc1a541026dc24be5b0ffd8be66d977 \ No newline at end of file From 5bb5965710d0429c371e9a24c7865eae891bdfa8 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 30 Aug 2022 09:49:30 +0000 Subject: [PATCH 037/428] wasm util: remove superfluous function.bind() calls to eliminate a level of call() indirection. FossilOrigin-Name: 1aab9627983ef0f016b01f78564e79cf815ed14d4b1b6dc04ec627b96f1b2f70 --- ext/wasm/common/whwasmutil.js | 123 +++++++++++++++++----------------- manifest | 12 ++-- manifest.uuid | 2 +- 3 files changed, 68 insertions(+), 69 deletions(-) diff --git a/ext/wasm/common/whwasmutil.js b/ext/wasm/common/whwasmutil.js index 3fef06f724..38adc76c2f 100644 --- a/ext/wasm/common/whwasmutil.js +++ b/ext/wasm/common/whwasmutil.js @@ -326,7 +326,7 @@ self.WhWasmUtilInstaller = function(target){ if(c.HEAP64) return unsigned ? c.HEAP64U : c.HEAP64; break; default: - if(this.bigIntEnabled){ + if(target.bigIntEnabled){ if(n===self['BigUint64Array']) return c.HEAP64U; else if(n===self['BigInt64Array']) return c.HEAP64; break; @@ -334,7 +334,7 @@ self.WhWasmUtilInstaller = function(target){ } toss("Invalid heapForSize() size: expecting 8, 16, 32,", "or (if BigInt is enabled) 64."); - }.bind(target); + }; /** Returns the WASM-exported "indirect function table." @@ -346,16 +346,16 @@ self.WhWasmUtilInstaller = function(target){ - Use `__indirect_function_table` as the import name for the table, which is what LLVM does. */ - }.bind(target); + }; /** Given a function pointer, returns the WASM function table entry if found, else returns a falsy value. */ target.functionEntry = function(fptr){ - const ft = this.functionTable(); + const ft = target.functionTable(); return fptr < ft.length ? ft.get(fptr) : undefined; - }.bind(target); + }; /** Creates a WASM function which wraps the given JS function and @@ -504,7 +504,7 @@ self.WhWasmUtilInstaller = function(target){ https://github.com/emscripten-core/emscripten/issues/17323 */ target.installFunction = function f(func, sig){ - const ft = this.functionTable(); + const ft = target.functionTable(); const oldLen = ft.length; let ptr; while(cache.freeFuncIndexes.length){ @@ -532,13 +532,13 @@ self.WhWasmUtilInstaller = function(target){ } // It's not a WASM-exported function, so compile one... try { - ft.set(ptr, this.jsFuncToWasm(func, sig)); + ft.set(ptr, target.jsFuncToWasm(func, sig)); }catch(e){ if(ptr===oldLen) cache.freeFuncIndexes.push(oldLen); throw e; } return ptr; - }.bind(target); + }; /** Requires a pointer value previously returned from @@ -551,12 +551,12 @@ self.WhWasmUtilInstaller = function(target){ */ target.uninstallFunction = function(ptr){ const fi = cache.freeFuncIndexes; - const ft = this.functionTable(); + const ft = target.functionTable(); fi.push(ptr); const rc = ft.get(ptr); ft.set(ptr, null); return rc; - }.bind(target); + }; /** Given a WASM heap memory address and a data type name in the form @@ -614,14 +614,14 @@ self.WhWasmUtilInstaller = function(target){ case 'i16': return c.HEAP16[ptr>>1]; case 'i32': return c.HEAP32[ptr>>2]; case 'i64': - if(this.bigIntEnabled) return BigInt(c.HEAP64[ptr>>3]); + if(target.bigIntEnabled) return BigInt(c.HEAP64[ptr>>3]); break; case 'float': case 'f32': return c.HEAP32F[ptr>>2]; case 'double': case 'f64': return Number(c.HEAP64F[ptr>>3]); default: break; } toss('Invalid type for getMemValue():',type); - }.bind(target); + }; /** The counterpart of getMemValue(), this sets a numeric value at @@ -698,9 +698,9 @@ self.WhWasmUtilInstaller = function(target){ ptr is falsy, `null` is returned. */ target.cstringToJs = function(ptr){ - const n = this.cstrlen(ptr); + const n = target.cstrlen(ptr); return n ? __utf8Decode(heapWrappers().HEAP8U, ptr, ptr+n) : (null===n ? n : ""); - }.bind(target); + }; /** Given a JS string, this function returns its UTF-8 length in @@ -828,16 +828,16 @@ self.WhWasmUtilInstaller = function(target){ */ target.cstrncpy = function(tgtPtr, srcPtr, n){ if(!tgtPtr || !srcPtr) toss("cstrncpy() does not accept NULL strings."); - if(n<0) n = this.cstrlen(strPtr)+1; + if(n<0) n = target.cstrlen(strPtr)+1; else if(!(n>0)) return 0; - const heap = this.heap8u(); + const heap = target.heap8u(); let i = 0, ch; for(; i < n && (ch = heap[srcPtr+i]); ++i){ heap[tgtPtr+i] = ch; } if(ixcv.arg[t] || toss("Argument adapter not found:",t); - const __xResultAdapter = + const __xResultAdapterCheck = (t)=>xcv.result[t] || toss("Result adapter not found:",t); - cache.xWrap.convertArg = (t,v)=>__xArgAdapter(t)(v); + cache.xWrap.convertArg = (t,v)=>__xArgAdapterCheck(t)(v); cache.xWrap.convertResult = - (t,v)=>(null===t ? v : (t ? __xResultAdapter(t)(v) : undefined)); + (t,v)=>(null===t ? v : (t ? __xResultAdapterCheck(t)(v) : undefined)); /** Creates a wrapper for the WASM-exported function fname. Uses @@ -1327,34 +1327,32 @@ self.WhWasmUtilInstaller = function(target){ if(3===arguments.length && Array.isArray(arguments[2])){ argTypes = arguments[2]; } - const xf = this.xGet(fname); - if(argTypes.length!==xf.length) __argcMismatch(fname, xf.length) + const xf = target.xGet(fname); + if(argTypes.length!==xf.length) __argcMismatch(fname, xf.length); if((null===resultType) && 0===xf.length){ /* Func taking no args with an as-is return. We don't need a wrapper. */ return xf; } /*Verify the arg type conversions are valid...*/; - if(undefined!==resultType && null!==resultType) __xResultAdapter(resultType); - argTypes.forEach(__xArgAdapter) + if(undefined!==resultType && null!==resultType) __xResultAdapterCheck(resultType); + argTypes.forEach(__xArgAdapterCheck); if(0===xf.length){ // No args to convert, so we can create a simpler wrapper... - return function(){ - return (arguments.length - ? __argcMismatch(fname, xf.length) - : cache.xWrap.convertResult(resultType, xf.call(null))); - }; + return (...args)=>(args.length + ? __argcMismatch(fname, xf.length) + : cache.xWrap.convertResult(resultType, xf.call(null))); } return function(...args){ if(args.length!==xf.length) __argcMismatch(fname, xf.length); - const scope = this.scopedAllocPush(); + const scope = target.scopedAllocPush(); try{ const rc = xf.apply(null,args.map((v,i)=>cache.xWrap.convertArg(argTypes[i], v))); return cache.xWrap.convertResult(resultType, rc); }finally{ - this.scopedAllocPop(scope); + target.scopedAllocPop(scope); } - }.bind(this); - }.bind(target)/*xWrap()*/; + }; + }/*xWrap()*/; /** Internal impl for xWrap.resultAdapter() and argAdaptor(). */ const __xAdapter = function(func, argc, typeName, adapter, modeName, xcvPart){ @@ -1458,8 +1456,8 @@ self.WhWasmUtilInstaller = function(target){ */ target.xCallWrapped = function(fname, resultType, argTypes, ...args){ if(Array.isArray(arguments[3])) args = arguments[3]; - return this.xWrap(fname, resultType, argTypes||[]).apply(null, args||[]); - }.bind(target); + return target.xWrap(fname, resultType, argTypes||[]).apply(null, args||[]); + }; return target; }; @@ -1539,10 +1537,11 @@ self.WhWasmUtilInstaller.yawl = function(config){ || toss("Missing 'memory' object!"); } if(!tgt.alloc && arg.instance.exports.malloc){ + const exports = arg.instance.exports; tgt.alloc = function(n){ - return this(n) || toss("Allocation of",n,"bytes failed."); - }.bind(arg.instance.exports.malloc); - tgt.dealloc = function(m){this(m)}.bind(arg.instance.exports.free); + return exports.malloc(n) || toss("Allocation of",n,"bytes failed."); + }; + tgt.dealloc = function(m){exports.free(m)}; } wui(tgt); } diff --git a/manifest b/manifest index bfcc12ac8f..11a40f3737 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Lots\sof\stweaking\sin\sbatch-runner.js.\sMinor\sinternal\sAPI\supdate\sin\sOO\s#1\sAPI. -D 2022-08-29T18:58:38.025 +C wasm\sutil:\sremove\ssuperfluous\sfunction.bind()\scalls\sto\seliminate\sa\slevel\sof\scall()\sindirection. +D 2022-08-30T09:49:30.007 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -494,7 +494,7 @@ F ext/wasm/batch-runner.js 84a465acde760de81d0372415cce56737799876395a878c44a7d3 F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 -F ext/wasm/common/whwasmutil.js e1c6779c977c8614053e24311d489dd4872962cb58c27013d4778f0dd7329e9c +F ext/wasm/common/whwasmutil.js 3d68a9cd87a2d8f7e6d5a78f3b829a927f2e28422685e029139f6e75d4ed42e4 F ext/wasm/demo-oo1.html 75646855b38405d82781246fd08c852a2b3bee05dd9f0fe10ab655a8cffb79aa F ext/wasm/demo-oo1.js aad38cb90b6fa7fd4d1184e759b25056fb4ed45c4957c458896354281259515f F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f @@ -2012,8 +2012,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e5b7006f0f57f10a490d7eaeb7df77251a2f684602fed8ff161d8ce60033e7bc -R ac09921b66efbf8cc7311954674841d9 +P 24b82b9504db3d8e1335c2300b133f897dc1a541026dc24be5b0ffd8be66d977 +R c9f285330e60c2a709dc88c6f308d17d U stephan -Z 16cfbad53a69907d89a4919845ccdd35 +Z 746ec57f2eb86d7d664aa6615f7c7bf3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 622070996d..fe3f5cacb1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -24b82b9504db3d8e1335c2300b133f897dc1a541026dc24be5b0ffd8be66d977 \ No newline at end of file +1aab9627983ef0f016b01f78564e79cf815ed14d4b1b6dc04ec627b96f1b2f70 \ No newline at end of file From 429899ddad8acac2346874184d263f438ac9b4b6 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 30 Aug 2022 09:51:24 +0000 Subject: [PATCH 038/428] Remove the .timer script entry from fiddle app because emscripten has removed getrusage(), which breaks the timer. FossilOrigin-Name: 9034f19ae50f196cc2b94478edfcc8d765b08bcf383166f6212b3522dea63c01 --- ext/wasm/fiddle/fiddle.js | 3 ++- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/ext/wasm/fiddle/fiddle.js b/ext/wasm/fiddle/fiddle.js index 3fd36bff86..ec56dd5930 100644 --- a/ext/wasm/fiddle/fiddle.js +++ b/ext/wasm/fiddle/fiddle.js @@ -730,7 +730,8 @@ -- only that part is executed. -- ================================================ .help`}, - {name: "Timer on", sql: ".timer on"}, + //{name: "Timer on", sql: ".timer on"}, + // ^^^ re-enable if emscripten re-enables getrusage() {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); diff --git a/manifest b/manifest index 11a40f3737..97a8f12651 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C wasm\sutil:\sremove\ssuperfluous\sfunction.bind()\scalls\sto\seliminate\sa\slevel\sof\scall()\sindirection. -D 2022-08-30T09:49:30.007 +C Remove\sthe\s.timer\sscript\sentry\sfrom\sfiddle\sapp\sbecause\semscripten\shas\sremoved\sgetrusage(),\swhich\sbreaks\sthe\stimer. +D 2022-08-30T09:51:24.863 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -500,7 +500,7 @@ F ext/wasm/demo-oo1.js aad38cb90b6fa7fd4d1184e759b25056fb4ed45c4957c458896354281 F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 -F ext/wasm/fiddle/fiddle.js bef4b30e078445a7cd2255fba07acd083aa1c3cc074a73b38ea847fd340f1adc +F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f @@ -2012,8 +2012,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 24b82b9504db3d8e1335c2300b133f897dc1a541026dc24be5b0ffd8be66d977 -R c9f285330e60c2a709dc88c6f308d17d +P 1aab9627983ef0f016b01f78564e79cf815ed14d4b1b6dc04ec627b96f1b2f70 +R 7a91cd8f075d71f1129b2c16dc0c7e0d U stephan -Z 746ec57f2eb86d7d664aa6615f7c7bf3 +Z fa99287ead67c43b637178f52d6fe01b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index fe3f5cacb1..ef13e59932 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1aab9627983ef0f016b01f78564e79cf815ed14d4b1b6dc04ec627b96f1b2f70 \ No newline at end of file +9034f19ae50f196cc2b94478edfcc8d765b08bcf383166f6212b3522dea63c01 \ No newline at end of file From fbf0f488cfe0a240ca219969a334d498a30769b5 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 30 Aug 2022 10:04:08 +0000 Subject: [PATCH 039/428] batch-runner.js: move generated SQL files into ./sql and start adding hand-written ones (like the Mandelbrot) to that set. FossilOrigin-Name: 06c106a7d23e4486dbed092757b7588688226ad35539ecc31378a8497f59d1ec --- ext/wasm/GNUmakefile | 15 ++++++++------- ext/wasm/batch-runner.js | 3 ++- ext/wasm/split-speedtest1-script.sh | 4 +++- ext/wasm/sql/000-mandelbrot.sql | 17 +++++++++++++++++ manifest | 17 +++++++++-------- manifest.uuid | 2 +- 6 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 ext/wasm/sql/000-mandelbrot.sql diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 6a0835ece4..1067018d4a 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -289,18 +289,19 @@ all: $(sqlite3.js) ######################################################################## # Bits for use with batch-runner.js... +dir.sql := sql speedtest1 := ../../speedtest1 +speedtest1.sql := $(dir.sql)/speedtest1.sql $(speedtest1): $(MAKE) -C ../.. speedtest1 -speedtest1.sql: $(speedtest1) +$(speedtest1.sql): $(speedtest1) $(speedtest1) --script $@ -speedtest1-000.sql: - echo "select 1;" > $@ -batch-runner.list: $(MAKEFILE) speedtest1.sql speedtest1-000.sql - bash split-speedtest1-script.sh speedtest1.sql - ls -1 *.sql | grep -v speedtest1.sql | sort > $@ -CLEAN_FILES += batch-runner.list speedtest1*.sql +batch-runner.list: $(MAKEFILE) $(speedtest1.sql) $(dir.sql)/000-mandelbrot.sql + bash split-speedtest1-script.sh $(dir.sql)/speedtest1.sql + ls -1 $(dir.sql)/*.sql | grep -v speedtest1.sql | sort > $@ +CLEAN_FILES += batch-runner.list $(dir.sql)/speedtest1*.sql batch: batch-runner.list +all: batch ######################################################################## # fiddle_remote is the remote destination for the fiddle app. It diff --git a/ext/wasm/batch-runner.js b/ext/wasm/batch-runner.js index 0f5406f902..71171c3236 100644 --- a/ext/wasm/batch-runner.js +++ b/ext/wasm/batch-runner.js @@ -119,7 +119,8 @@ list.forEach(function(fn){ if(!fn) return; opt = document.createElement('option'); - opt.value = opt.innerText = fn; + opt.value = fn; + opt.innerText = fn.split('/').pop(); sel.appendChild(opt); }); this.logHtml("Loaded",infile); diff --git a/ext/wasm/split-speedtest1-script.sh b/ext/wasm/split-speedtest1-script.sh index e228a872ec..e072d08a1e 100755 --- a/ext/wasm/split-speedtest1-script.sh +++ b/ext/wasm/split-speedtest1-script.sh @@ -7,9 +7,11 @@ if [ x = "x${testnums}" ]; then echo "Could not parse any begin/end blocks out of $infile" 1>&2 exit 1 fi +odir=${infile%%/*} +if [ "$odir" = "$infile" ]; then odir="."; fi #echo testnums=$testnums for n in $testnums; do - ofile=$(printf "speedtest1-%03d.sql" $n) + ofile=$odir/$(printf "speedtest1-%03d.sql" $n) sed -n -e "/^-- begin test $n /,/^-- end test $n\$/p" $infile > $ofile echo -e "$n\t$ofile" done diff --git a/ext/wasm/sql/000-mandelbrot.sql b/ext/wasm/sql/000-mandelbrot.sql new file mode 100644 index 0000000000..3aa5f57156 --- /dev/null +++ b/ext/wasm/sql/000-mandelbrot.sql @@ -0,0 +1,17 @@ +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; diff --git a/manifest b/manifest index 97a8f12651..f8f5e73b26 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sthe\s.timer\sscript\sentry\sfrom\sfiddle\sapp\sbecause\semscripten\shas\sremoved\sgetrusage(),\swhich\sbreaks\sthe\stimer. -D 2022-08-30T09:51:24.863 +C batch-runner.js:\smove\sgenerated\sSQL\sfiles\sinto\s./sql\sand\sstart\sadding\shand-written\sones\s(like\sthe\sMandelbrot)\sto\sthat\sset. +D 2022-08-30T10:04:08.374 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile cabfbb177d16c550442313416698e66dde575df27177d4ca3ccece5a66b37de4 +F ext/wasm/GNUmakefile b5d1e285ed9814646e1fb12a27b7507aea2d7208ef76e486e1471c8aabac5226 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -490,7 +490,7 @@ F ext/wasm/api/sqlite3-api-worker1.js 73579555563b789785ae83724014eaf31811073aad F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 F ext/wasm/batch-runner.html e5c3edd4a6c9359f6d9e6c99cb5f87f09007d98fa1c705ed3efa370abcd4323e -F ext/wasm/batch-runner.js 84a465acde760de81d0372415cce56737799876395a878c44a7d3ce5dfe29e39 +F ext/wasm/batch-runner.js e5b4b93b8008b2cc53694025042b7322267790063d1f0c96cf1b17e9d5a75c7d F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 @@ -510,7 +510,8 @@ F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb43 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0 -F ext/wasm/split-speedtest1-script.sh 7138e474122686fecb274d78050f9bee6525bc64b72ff1af39f6fc0753ccfc1f x +F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x +F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 @@ -2012,8 +2013,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 1aab9627983ef0f016b01f78564e79cf815ed14d4b1b6dc04ec627b96f1b2f70 -R 7a91cd8f075d71f1129b2c16dc0c7e0d +P 9034f19ae50f196cc2b94478edfcc8d765b08bcf383166f6212b3522dea63c01 +R 3fee411065930bf42f56509feb119f8a U stephan -Z fa99287ead67c43b637178f52d6fe01b +Z 41ba67db7a03bd9c0e2ce1f308cc01b3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ef13e59932..f8b12461be 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9034f19ae50f196cc2b94478edfcc8d765b08bcf383166f6212b3522dea63c01 \ No newline at end of file +06c106a7d23e4486dbed092757b7588688226ad35539ecc31378a8497f59d1ec \ No newline at end of file From ffc0cbb0248123ea635ad83428d5b4be11a8508b Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 30 Aug 2022 10:26:33 +0000 Subject: [PATCH 040/428] batch-runner.js: re-enable unlink-before-run so that OPFS speedtest1 batch can work. FossilOrigin-Name: ba08f7a7f12601aa580a8dc493b43cdd5ab65c87d5e4b3fd2f549343e45c2d2a --- ext/wasm/batch-runner.js | 4 ++-- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/wasm/batch-runner.js b/ext/wasm/batch-runner.js index 71171c3236..cbd712bb91 100644 --- a/ext/wasm/batch-runner.js +++ b/ext/wasm/batch-runner.js @@ -57,9 +57,9 @@ const stack = wasm.scopedAllocPush(); let pDb = 0; try{ - /*if(unlinkFirst && fn && ':memory:'!==fn){ + if(unlinkFirst && fn && ':memory:'!==fn){ capi.sqlite3_wasm_vfs_unlink(fn); - }*/ + } const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; const ppDb = wasm.scopedAllocPtr(); const rc = capi.sqlite3_open_v2(fn, ppDb, oFlags, null); diff --git a/manifest b/manifest index f8f5e73b26..28e7c2d2de 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C batch-runner.js:\smove\sgenerated\sSQL\sfiles\sinto\s./sql\sand\sstart\sadding\shand-written\sones\s(like\sthe\sMandelbrot)\sto\sthat\sset. -D 2022-08-30T10:04:08.374 +C batch-runner.js:\sre-enable\sunlink-before-run\sso\sthat\sOPFS\sspeedtest1\sbatch\scan\swork. +D 2022-08-30T10:26:33.223 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -490,7 +490,7 @@ F ext/wasm/api/sqlite3-api-worker1.js 73579555563b789785ae83724014eaf31811073aad F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 F ext/wasm/batch-runner.html e5c3edd4a6c9359f6d9e6c99cb5f87f09007d98fa1c705ed3efa370abcd4323e -F ext/wasm/batch-runner.js e5b4b93b8008b2cc53694025042b7322267790063d1f0c96cf1b17e9d5a75c7d +F ext/wasm/batch-runner.js da471fb7b5c6c918cfe59adaebedc3af24a1268084d09d90143653c71b2493dd F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 @@ -2013,8 +2013,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 9034f19ae50f196cc2b94478edfcc8d765b08bcf383166f6212b3522dea63c01 -R 3fee411065930bf42f56509feb119f8a +P 06c106a7d23e4486dbed092757b7588688226ad35539ecc31378a8497f59d1ec +R 4e67c8f75eba175d0066f0954616ff1b U stephan -Z 41ba67db7a03bd9c0e2ce1f308cc01b3 +Z 9d799461f6bdad041adab908a7c4bd92 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f8b12461be..50b5203520 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -06c106a7d23e4486dbed092757b7588688226ad35539ecc31378a8497f59d1ec \ No newline at end of file +ba08f7a7f12601aa580a8dc493b43cdd5ab65c87d5e4b3fd2f549343e45c2d2a \ No newline at end of file From 53f635df55ecfce3baf9fccc1909dad53bd9643e Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 30 Aug 2022 17:57:50 +0000 Subject: [PATCH 041/428] batch-runner.js: add rudimentary metrics export to CSV. Add a toggle to reverse the log output mode (in normal order or most recent first). FossilOrigin-Name: b26e2bc05537a1f451db1bdd36a824d71c3ea25a130b6285f31ff9799d65fa7a --- ext/wasm/GNUmakefile | 6 +- ext/wasm/batch-runner.html | 23 +++++++- ext/wasm/batch-runner.js | 111 +++++++++++++++++++++++++++++++++++-- manifest | 16 +++--- manifest.uuid | 2 +- 5 files changed, 141 insertions(+), 17 deletions(-) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 1067018d4a..39cf8b26e4 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -62,11 +62,14 @@ $(dir.top)/sqlite3.c: emcc_opt ?= -O0 .PHONY: release release: - $(MAKE) 'emcc_opt=-Os -g3' + $(MAKE) 'emcc_opt=-Os -g3 -flto' # ^^^^^ target-specific vars, e.g.: # release: emcc_opt=... # apparently only work for file targets, not PHONY targets? # +# ^^^ -flto improves runtime speed at -O0 considerably but doubles +# build time. +# # ^^^^ -O3, -Oz, -Os minify symbol names and there appears to be no # way around that except to use -g3, but -g3 causes the binary file # size to absolutely explode (approx. 5x larger). This minification @@ -284,6 +287,7 @@ endif CLEAN_FILES += $(sqlite3.js) $(sqlite3.wasm) all: $(sqlite3.js) +wasm: $(sqlite3.js) # End main Emscripten-based module build ######################################################################## diff --git a/ext/wasm/batch-runner.html b/ext/wasm/batch-runner.html index c91e591553..1b8d335ddb 100644 --- a/ext/wasm/batch-runner.html +++ b/ext/wasm/batch-runner.html @@ -42,11 +42,16 @@ + + + + +
-
(Log output is in reverse order, newest first!)
+
@@ -58,14 +63,26 @@ flex-direction: column; flex-wrap: wrap; } - .warning { color: firebrick; } + .warning { color: firebrick; } + .input-wrapper { + white-space: nowrap; + } #test-output { border: 1px inset; padding: 0.25em; /*max-height: 30em;*/ overflow: auto; white-space: break-spaces; - display: flex; flex-direction: column-reverse; + display: flex; flex-direction: column; + } + #test-output.reverse { + flex-direction: column-reverse; + } + .hidden { + position: absolute !important; + opacity: 0 !important; + pointer-events: none !important; + display: none !important; } diff --git a/ext/wasm/batch-runner.js b/ext/wasm/batch-runner.js index cbd712bb91..909a140047 100644 --- a/ext/wasm/batch-runner.js +++ b/ext/wasm/batch-runner.js @@ -26,10 +26,22 @@ btnRun: document.querySelector('#sql-run'), btnRunNext: document.querySelector('#sql-run-next'), btnRunRemaining: document.querySelector('#sql-run-remaining'), + btnExportMetrics: document.querySelector('#export-metrics'), btnClear: document.querySelector('#output-clear'), - btnReset: document.querySelector('#db-reset') + btnReset: document.querySelector('#db-reset'), + cbReverseLog: document.querySelector('#cb-reverse-log-order') }, cache:{}, + metrics:{ + /** + Map of sql-file to timing metrics. We currently only store + the most recent run of each file, but we really should store + all runs so that we can average out certain values which vary + significantly across runs. e.g. a mandelbrot-generating query + will have a wide range of runtimes when run 10 times in a + row. + */ + }, log: console.log.bind(console), warn: console.warn.bind(console), cls: function(){this.e.output.innerHTML = ''}, @@ -63,8 +75,11 @@ const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; const ppDb = wasm.scopedAllocPtr(); const rc = capi.sqlite3_open_v2(fn, ppDb, oFlags, null); - if(rc) toss("sqlite3_open_v2() failed with code",rc); pDb = wasm.getPtrValue(ppDb) + if(rc){ + if(pDb) capi.sqlite3_close_v2(pDb); + toss("sqlite3_open_v2() failed with code",rc); + } }finally{ wasm.scopedAllocPop(stack); } @@ -84,6 +99,12 @@ } }, + /** + Loads batch-runner.list and populates the selection list from + it. Returns a promise which resolves to nothing in particular + when it completes. Only intended to be run once at the start + of the app. + */ loadSqlList: async function(){ const sel = this.e.selSql; sel.innerHTML = ''; @@ -156,7 +177,62 @@ document.querySelectorAll('.disable-during-eval').forEach((e)=>e.disabled = disable); }, - /** Fetch ./fn and eval it as an SQL blob. */ + /** + Converts this.metrics() to a form which is suitable for easy conversion to + CSV. It returns an array of arrays. The first sub-array is the column names. + The 2nd and subsequent are the values, one per test file (only the most recent + metrics are kept for any given file). + */ + metricsToArrays: function(){ + const rc = []; + Object.keys(this.metrics).sort().forEach((k)=>{ + const m = this.metrics[k]; + delete m.evalFileStart; + delete m.evalFileEnd; + const mk = Object.keys(m).sort(); + if(!rc.length){ + rc.push(['file', ...mk]); + } + const row = [k.split('/').pop()/*remove dir prefix from filename*/]; + rc.push(row); + mk.forEach((kk)=>row.push(m[kk])); + }); + return rc; + }, + + metricsToBlob: function(colSeparator='\t'){ + const ar = [], ma = this.metricsToArrays(); + if(!ma.length){ + this.logErr("Metrics are empty. Run something."); + return; + } + ma.forEach(function(row){ + ar.push(row.join(colSeparator),'\n'); + }); + return new Blob(ar); + }, + + downloadMetrics: function(){ + const b = this.metricsToBlob(); + if(!b) return; + const url = URL.createObjectURL(b); + const a = document.createElement('a'); + a.href = url; + a.download = 'batch-runner-metrics-'+((new Date().getTime()/1000) | 0)+'.csv'; + this.logHtml("Triggering download of",a.download); + document.body.appendChild(a); + a.click(); + setTimeout(()=>{ + document.body.removeChild(a); + URL.revokeObjectURL(url); + }, 500); + }, + + /** + Fetch file fn and eval it as an SQL blob. This is an async + operation and returns a Promise which resolves to this + object on success. + */ evalFile: async function(fn){ const sql = await this.fetchFile(fn); const banner = "========================================"; @@ -165,9 +241,11 @@ const capi = this.sqlite3.capi, wasm = capi.wasm; let pStmt = 0, pSqlBegin; const stack = wasm.scopedAllocPush(); - const metrics = Object.create(null); + const metrics = this.metrics[fn] = Object.create(null); metrics.prepTotal = metrics.stepTotal = 0; metrics.stmtCount = 0; + metrics.malloc = 0; + metrics.strcpy = 0; this.blockControls(true); if(this.gotErr){ this.logErr("Cannot run ["+fn+"]: error cleanup is pending."); @@ -180,11 +258,16 @@ let t; let sqlByteLen = sql.byteLength; const [ppStmt, pzTail] = wasm.scopedAllocPtr(2); + t = performance.now(); pSqlBegin = wasm.alloc( sqlByteLen + 1/*SQL + NUL*/) || toss("alloc(",sqlByteLen,") failed"); + metrics.malloc = performance.now() - t; + metrics.byteLength = sqlByteLen; let pSql = pSqlBegin; const pSqlEnd = pSqlBegin + sqlByteLen; + t = performance.now(); wasm.heap8().set(sql, pSql); wasm.setMemValue(pSql + sqlByteLen, 0); + metrics.strcpy = performance.now() - t; let breaker = 0; while(pSql && wasm.getMemValue(pSql,'i8')){ wasm.setPtrValue(ppStmt, 0); @@ -261,6 +344,20 @@ } this.openDb(dbFile, !!pDir); const who = this; + const eReverseLogNotice = document.querySelector('#reverse-log-notice'); + if(this.e.cbReverseLog.checked){ + eReverseLogNotice.classList.remove('hidden'); + this.e.output.classList.add('reverse'); + } + this.e.cbReverseLog.addEventListener('change', function(){ + if(this.checked){ + who.e.output.classList.add('reverse'); + eReverseLogNotice.classList.remove('hidden'); + }else{ + who.e.output.classList.remove('reverse'); + eReverseLogNotice.classList.add('hidden'); + } + }, false); this.e.btnClear.addEventListener('click', ()=>this.cls(), false); this.e.btnRun.addEventListener('click', function(){ if(!who.e.selSql.value) return; @@ -278,6 +375,12 @@ who.openDb(fn,true); } }, false); + this.e.btnExportMetrics.addEventListener('click', function(){ + who.logHtml2('warning',"Metrics export is a Work in Progress. See output in dev console."); + who.downloadMetrics(); + const m = who.metricsToArrays(); + console.log("Metrics:",who.metrics, m); + }); this.e.btnRunRemaining.addEventListener('click', async function(){ let v = who.e.selSql.value; const timeStart = performance.now(); diff --git a/manifest b/manifest index 28e7c2d2de..d6ecf92b24 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C batch-runner.js:\sre-enable\sunlink-before-run\sso\sthat\sOPFS\sspeedtest1\sbatch\scan\swork. -D 2022-08-30T10:26:33.223 +C batch-runner.js:\sadd\srudimentary\smetrics\sexport\sto\sCSV.\sAdd\sa\stoggle\sto\sreverse\sthe\slog\soutput\smode\s(in\snormal\sorder\sor\smost\srecent\sfirst). +D 2022-08-30T17:57:50.778 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile b5d1e285ed9814646e1fb12a27b7507aea2d7208ef76e486e1471c8aabac5226 +F ext/wasm/GNUmakefile fd1ba419c38d79f742849cc96a1f1e1dd63821e6173bc5727f0a5439f04b4131 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -489,8 +489,8 @@ F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db F ext/wasm/api/sqlite3-api-worker1.js 73579555563b789785ae83724014eaf31811073aad9be6596c8336ffb51edd71 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 -F ext/wasm/batch-runner.html e5c3edd4a6c9359f6d9e6c99cb5f87f09007d98fa1c705ed3efa370abcd4323e -F ext/wasm/batch-runner.js da471fb7b5c6c918cfe59adaebedc3af24a1268084d09d90143653c71b2493dd +F ext/wasm/batch-runner.html 439245594711981831f2775f1426fd20d73a9b1bbf53f2cbbfc1194fa069891c +F ext/wasm/batch-runner.js dfe194ff87dac20e641aeee7080b74a877ca4a24ea107d6a80ff8115b28b7f0b F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 @@ -2013,8 +2013,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 06c106a7d23e4486dbed092757b7588688226ad35539ecc31378a8497f59d1ec -R 4e67c8f75eba175d0066f0954616ff1b +P ba08f7a7f12601aa580a8dc493b43cdd5ab65c87d5e4b3fd2f549343e45c2d2a +R cd46865d209bc499b87d03a53afeb5f7 U stephan -Z 9d799461f6bdad041adab908a7c4bd92 +Z a638c92d95e9388486f91bc003769b2d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 50b5203520..9cdf828810 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ba08f7a7f12601aa580a8dc493b43cdd5ab65c87d5e4b3fd2f549343e45c2d2a \ No newline at end of file +b26e2bc05537a1f451db1bdd36a824d71c3ea25a130b6285f31ff9799d65fa7a \ No newline at end of file From 73b09b87d5a7c9e35af045b5b59cbd49756cb4df Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 31 Aug 2022 20:45:43 +0000 Subject: [PATCH 042/428] Add new files for an extension to recover data from corrupted databases. FossilOrigin-Name: f8298eeba01cb5b02ac4d642c06f3801331ca90edea533ea898a3283981a9e49 --- ext/misc/dbdata.c | 31 +- ext/recover/recover1.test | 116 ++++++ ext/recover/recover_common.tcl | 5 + ext/recover/sqlite3recover.c | 728 +++++++++++++++++++++++++++++++++ ext/recover/sqlite3recover.h | 66 +++ ext/recover/test_recover.c | 185 +++++++++ main.mk | 3 + manifest | 26 +- manifest.uuid | 2 +- src/test_tclsh.c | 2 + 10 files changed, 1152 insertions(+), 12 deletions(-) create mode 100644 ext/recover/recover1.test create mode 100644 ext/recover/recover_common.tcl create mode 100644 ext/recover/sqlite3recover.c create mode 100644 ext/recover/sqlite3recover.h create mode 100644 ext/recover/test_recover.c diff --git a/ext/misc/dbdata.c b/ext/misc/dbdata.c index 7405e7c890..b79eafce7c 100644 --- a/ext/misc/dbdata.c +++ b/ext/misc/dbdata.c @@ -660,6 +660,18 @@ static int dbdataEof(sqlite3_vtab_cursor *pCursor){ return pCsr->aPage==0; } +/* +** Return true if nul-terminated string zSchema ends in "()". Or false +** otherwise. +*/ +static int dbdataIsFunction(const char *zSchema){ + int n = strlen(zSchema); + if( n>2 && zSchema[n-2]=='(' && zSchema[n-1]==')' ){ + return n-2; + } + return 0; +} + /* ** Determine the size in pages of database zSchema (where zSchema is ** "main", "temp" or the name of an attached database) and set @@ -670,10 +682,16 @@ static int dbdataDbsize(DbdataCursor *pCsr, const char *zSchema){ DbdataTable *pTab = (DbdataTable*)pCsr->base.pVtab; char *zSql = 0; int rc, rc2; + int nFunc = 0; sqlite3_stmt *pStmt = 0; - zSql = sqlite3_mprintf("PRAGMA %Q.page_count", zSchema); + if( (nFunc = dbdataIsFunction(zSchema))>0 ){ + zSql = sqlite3_mprintf("SELECT %.*s(0)", nFunc, zSchema); + }else{ + zSql = sqlite3_mprintf("PRAGMA %Q.page_count", zSchema); + } if( zSql==0 ) return SQLITE_NOMEM; + rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ @@ -711,9 +729,18 @@ static int dbdataFilter( } if( rc==SQLITE_OK ){ + int nFunc = 0; if( pTab->pStmt ){ pCsr->pStmt = pTab->pStmt; pTab->pStmt = 0; + }else if( (nFunc = dbdataIsFunction(zSchema))>0 ){ + char *zSql = sqlite3_mprintf("SELECT %.*s(?2)", nFunc, zSchema); + if( zSql==0 ){ + rc = SQLITE_NOMEM; + }else{ + rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0); + sqlite3_free(zSql); + } }else{ rc = sqlite3_prepare_v2(pTab->db, "SELECT data FROM sqlite_dbpage(?) WHERE pgno=?", -1, @@ -732,7 +759,7 @@ static int dbdataFilter( return rc; } -/* +/* ** Return a column for the sqlite_dbdata or sqlite_dbptr table. */ static int dbdataColumn( diff --git a/ext/recover/recover1.test b/ext/recover/recover1.test new file mode 100644 index 0000000000..c4348baea0 --- /dev/null +++ b/ext/recover/recover1.test @@ -0,0 +1,116 @@ +# 2022 August 28 +# +# 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. +# +#*********************************************************************** +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] recover_common.tcl] +source $testdir/tester.tcl + +set testprefix recover1 + + + +proc compare_result {db1 db2 sql} { + set r1 [$db1 eval $sql] + set r2 [$db2 eval $sql] + if {$r1 != $r2} { + puts "r1: $r1" + puts "r2: $r2" + error "mismatch for $sql" + } + return "" +} + +proc compare_dbs {db1 db2} { + compare_result $db1 $db2 "SELECT sql FROM sqlite_master ORDER BY 1" + foreach tbl [$db1 eval {SELECT name FROM sqlite_master WHERE type='table'}] { + compare_result $db1 $db2 "SELECT * FROM $tbl" + } +} + +proc do_recover_test {tn} { + forcedelete test.db2 + + uplevel [list do_test $tn.1 { + set R [sqlite3_recover_init db main test.db2] + $R step + $R finish + } {}] + + sqlite3 db2 test.db2 + uplevel [list do_test $tn.2 [list compare_dbs db db2] {}] + db2 close +} + + +do_execsql_test 1.0 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + CREATE TABLE t2(a INTEGER PRIMARY KEY, b) WITHOUT ROWID; + WITH s(i) AS ( + SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10 + ) + INSERT INTO t1 SELECT i*2, hex(randomblob(250)) FROM s; + INSERT INTO t2 SELECT * FROM t1; +} + +do_recover_test 1 + +do_execsql_test 2.0 { + ALTER TABLE t1 ADD COLUMN c DEFAULT 'xyz' +} +do_recover_test 2 + +do_execsql_test 3.0 { + CREATE INDEX i1 ON t1(c); +} +do_recover_test 3 + +do_execsql_test 4.0 { + CREATE VIEW v1 AS SELECT * FROM t2; +} +do_recover_test 4 + +do_execsql_test 5.0 { + CREATE UNIQUE INDEX i2 ON t1(c, b); +} +do_recover_test 5 + +#-------------------------------------------------------------------------- +# +reset_db +do_execsql_test 6.0 { + CREATE TABLE t1( + a INTEGER PRIMARY KEY, + b INT, + c TEXT, + d INT GENERATED ALWAYS AS (a*abs(b)) VIRTUAL, + e TEXT GENERATED ALWAYS AS (substr(c,b,b+1)) STORED, + f TEXT GENERATED ALWAYS AS (substr(c,b,b+1)) STORED + ); + + INSERT INTO t1 VALUES(1, 2, 'hello world'); +} +do_recover_test 6 + +do_execsql_test 7.0 { + CREATE TABLE t2(i, j GENERATED ALWAYS AS (i+1) STORED, k); + INSERT INTO t2 VALUES(10, 'ten'); +} +do_execsql_test 7.1 { + SELECT * FROM t2 +} {10 11 ten} + +do_recover_test 7.2 + +finish_test + diff --git a/ext/recover/recover_common.tcl b/ext/recover/recover_common.tcl new file mode 100644 index 0000000000..3f2ff2d6cc --- /dev/null +++ b/ext/recover/recover_common.tcl @@ -0,0 +1,5 @@ + + + + + diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c new file mode 100644 index 0000000000..6c7828c007 --- /dev/null +++ b/ext/recover/sqlite3recover.c @@ -0,0 +1,728 @@ +/* +** 2022-08-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. +** +************************************************************************* +** +*/ + + +#include "sqlite3recover.h" +#include +#include + +typedef unsigned int u32; +typedef sqlite3_int64 i64; + +typedef struct RecoverColumn RecoverColumn; +struct RecoverColumn { + char *zCol; + int eHidden; +}; + +#define RECOVER_EHIDDEN_NONE 0 +#define RECOVER_EHIDDEN_HIDDEN 1 +#define RECOVER_EHIDDEN_VIRTUAL 2 +#define RECOVER_EHIDDEN_STORED 3 + +/* +** When running the ".recover" command, each output table, and the special +** orphaned row table if it is required, is represented by an instance +** of the following struct. +*/ +typedef struct RecoverTable RecoverTable; +struct RecoverTable { + u32 iRoot; /* Root page in original database */ + char *zTab; /* Name of table */ + int nCol; /* Number of columns in table */ + RecoverColumn *aCol; /* Array of columns */ + int bIntkey; /* True for intkey, false for without rowid */ + int iPk; /* Index of IPK column, if bIntkey */ + + RecoverTable *pNext; +}; + +/* +** +*/ +#define RECOVERY_SCHEMA \ +" CREATE TABLE recovery.freelist(" \ +" pgno INTEGER PRIMARY KEY" \ +" );" \ +" CREATE TABLE recovery.dbptr(" \ +" pgno, child, PRIMARY KEY(child, pgno)" \ +" ) WITHOUT ROWID;" \ +" CREATE TABLE recovery.map(" \ +" pgno INTEGER PRIMARY KEY, maxlen INT, intkey, root INT" \ +" );" \ +" CREATE TABLE recovery.schema(" \ +" type, name, tbl_name, rootpage, sql" \ +" );" + + +struct sqlite3_recover { + sqlite3 *dbIn; + sqlite3 *dbOut; + + sqlite3_stmt *pGetPage; + + char *zDb; + char *zUri; + RecoverTable *pTblList; + + int errCode; /* For sqlite3_recover_errcode() */ + char *zErrMsg; /* For sqlite3_recover_errmsg() */ + + char *zStateDb; +}; + +/* +** Like strlen(). But handles NULL pointer arguments. +*/ +static int recoverStrlen(const char *zStr){ + int nRet = 0; + if( zStr ){ + while( zStr[nRet] ) nRet++; + } + return nRet; +} + +static void *recoverMalloc(sqlite3_recover *p, sqlite3_int64 nByte){ + void *pRet = 0; + assert( nByte>0 ); + if( p->errCode==SQLITE_OK ){ + pRet = sqlite3_malloc64(nByte); + if( pRet ){ + memset(pRet, 0, nByte); + }else{ + p->errCode = SQLITE_NOMEM; + } + } + return pRet; +} + +static int recoverError( + sqlite3_recover *p, + int errCode, + const char *zFmt, ... +){ + va_list ap; + char *z; + va_start(ap, zFmt); + z = sqlite3_vmprintf(zFmt, ap); + va_end(ap); + + sqlite3_free(p->zErrMsg); + p->zErrMsg = z; + p->errCode = errCode; + return errCode; +} + +static int recoverDbError(sqlite3_recover *p, sqlite3 *db){ + return recoverError(p, sqlite3_errcode(db), "%s", sqlite3_errmsg(db)); +} + +static sqlite3_stmt *recoverPrepare( + sqlite3_recover *p, + sqlite3 *db, + const char *zSql +){ + sqlite3_stmt *pStmt = 0; + if( p->errCode==SQLITE_OK ){ + if( sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0) ){ + recoverDbError(p, db); + } + } + return pStmt; +} + +/* +** Create a prepared statement using printf-style arguments for the SQL. +*/ +static sqlite3_stmt *recoverPreparePrintf( + sqlite3_recover *p, + sqlite3 *db, + const char *zFmt, ... +){ + sqlite3_stmt *pStmt = 0; + if( p->errCode==SQLITE_OK ){ + va_list ap; + char *z; + va_start(ap, zFmt); + z = sqlite3_vmprintf(zFmt, ap); + va_end(ap); + if( z==0 ){ + p->errCode = SQLITE_NOMEM; + }else{ + pStmt = recoverPrepare(p, db, z); + sqlite3_free(z); + } + } + return pStmt; +} + + +static sqlite3_stmt *recoverReset(sqlite3_recover *p, sqlite3_stmt *pStmt){ + int rc = sqlite3_reset(pStmt); + if( rc!=SQLITE_OK && p->errCode==SQLITE_OK ){ + recoverDbError(p, sqlite3_db_handle(pStmt)); + } + return pStmt; +} + +static void recoverFinalize(sqlite3_recover *p, sqlite3_stmt *pStmt){ + sqlite3 *db = sqlite3_db_handle(pStmt); + int rc = sqlite3_finalize(pStmt); + if( rc!=SQLITE_OK && p->errCode==SQLITE_OK ){ + recoverDbError(p, db); + } +} + +static int recoverExec(sqlite3_recover *p, sqlite3 *db, const char *zSql){ + if( p->errCode==SQLITE_OK ){ + int rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0); + if( rc ){ + recoverDbError(p, p->dbOut); + } + } + return p->errCode; +} + +/* +** The implementation of a user-defined SQL function invoked by the +** sqlite_dbdata and sqlite_dbptr virtual table modules to access pages +** of the database being recovered. +** +** This function always takes a single integer argument. If the arguement +** is zero, then the value returned is the number of pages in the db being +** recovered. If the argument is greater than zero, it is a page number. +** The value returned in this case is an SQL blob containing the data for +** the identified page of the db being recovered. e.g. +** +** SELECT getpage(0); -- return number of pages in db +** SELECT getpage(4); -- return page 4 of db as a blob of data +*/ +static void recoverGetPage( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ + sqlite3_recover *p = (sqlite3_recover*)sqlite3_user_data(pCtx); + sqlite3_int64 pgno = sqlite3_value_int64(apArg[0]); + sqlite3_stmt *pStmt = 0; + + assert( nArg==1 ); + if( pgno==0 ){ + pStmt = recoverPreparePrintf(p, p->dbIn, "PRAGMA %Q.page_count", p->zDb); + }else if( p->pGetPage==0 ){ + pStmt = recoverPreparePrintf( + p, p->dbIn, "SELECT data FROM sqlite_dbpage(%Q) WHERE pgno=?", p->zDb + ); + }else{ + pStmt = p->pGetPage; + } + + if( pStmt ){ + if( pgno ) sqlite3_bind_int64(pStmt, 1, pgno); + if( SQLITE_ROW==sqlite3_step(pStmt) ){ + sqlite3_result_value(pCtx, sqlite3_column_value(pStmt, 0)); + } + if( pgno ){ + p->pGetPage = recoverReset(p, pStmt); + }else{ + recoverFinalize(p, pStmt); + } + } + + if( p->errCode ){ + if( p->zErrMsg ) sqlite3_result_error(pCtx, p->zErrMsg, -1); + sqlite3_result_error_code(pCtx, p->errCode); + } +} + +#ifdef _WIN32 +__declspec(dllexport) +#endif +int sqlite3_dbdata_init(sqlite3*, char**, const sqlite3_api_routines*); + +static int recoverOpenOutput(sqlite3_recover *p){ + int rc = SQLITE_OK; + if( p->dbOut==0 ){ + const int flags = SQLITE_OPEN_URI|SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE; + sqlite3 *db = 0; + + assert( p->dbOut==0 ); + + rc = sqlite3_open_v2(p->zUri, &db, flags, 0); + if( rc==SQLITE_OK ){ + const char *zPath = p->zStateDb ? p->zStateDb : ":memory:"; + char *zSql = sqlite3_mprintf("ATTACH %Q AS recovery", zPath); + if( zSql==0 ){ + rc = p->errCode = SQLITE_NOMEM; + }else{ + rc = sqlite3_exec(db, zSql, 0, 0, 0); + } + sqlite3_free(zSql); + } + + if( rc==SQLITE_OK ){ + sqlite3_backup *pBackup = sqlite3_backup_init(db, "main", db, "recovery"); + if( pBackup ){ + while( sqlite3_backup_step(pBackup, 1000)==SQLITE_OK ); + rc = sqlite3_backup_finish(pBackup); + } + } + if( rc==SQLITE_OK ){ + rc = sqlite3_exec(db, RECOVERY_SCHEMA, 0, 0, 0); + } + + if( rc==SQLITE_OK ){ + sqlite3_dbdata_init(db, 0, 0); + rc = sqlite3_create_function( + db, "getpage", 1, SQLITE_UTF8, (void*)p, recoverGetPage, 0, 0 + ); + } + + if( rc!=SQLITE_OK ){ + if( p->errCode==SQLITE_OK ) rc = recoverDbError(p, db); + sqlite3_close(db); + }else{ + p->dbOut = db; + } + } + return rc; +} + +static int recoverCacheDbptr(sqlite3_recover *p){ + return recoverExec(p, p->dbOut, + "INSERT INTO recovery.dbptr " + "SELECT pgno, child FROM sqlite_dbptr('getpage()')" + ); +} + +static int recoverCacheSchema(sqlite3_recover *p){ + return recoverExec(p, p->dbOut, + "WITH RECURSIVE pages(p) AS (" + " SELECT 1" + " UNION" + " SELECT child FROM recovery.dbptr, pages WHERE pgno=p" + ")" + "INSERT INTO recovery.schema SELECT" + " max(CASE WHEN field=0 THEN value ELSE NULL END)," + " max(CASE WHEN field=1 THEN value ELSE NULL END)," + " max(CASE WHEN field=2 THEN value ELSE NULL END)," + " max(CASE WHEN field=3 THEN value ELSE NULL END)," + " max(CASE WHEN field=4 THEN value ELSE NULL END)" + "FROM sqlite_dbdata('getpage()') WHERE pgno IN (" + " SELECT p FROM pages" + ") GROUP BY pgno, cell" + ); +} + +static void recoverAddTable(sqlite3_recover *p, const char *zName, i64 iRoot){ + sqlite3_stmt *pStmt = recoverPreparePrintf(p, p->dbOut, + "PRAGMA table_xinfo(%Q)", zName + ); + + if( pStmt ){ + RecoverTable *pNew = 0; + int nCol = 0; + int nName = recoverStrlen(zName); + int nByte = 0; + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + nCol++; + nByte += (sqlite3_column_bytes(pStmt, 1)+1); + } + nByte += sizeof(RecoverTable) + nCol*sizeof(RecoverColumn) + nName+1; + recoverReset(p, pStmt); + + pNew = recoverMalloc(p, nByte); + if( pNew ){ + int i = 0; + char *csr = 0; + pNew->aCol = (RecoverColumn*)&pNew[1]; + pNew->zTab = csr = (char*)&pNew->aCol[nCol]; + pNew->nCol = nCol; + pNew->iRoot = iRoot; + pNew->iPk = -1; + memcpy(csr, zName, nName); + csr += nName+1; + + for(i=0; sqlite3_step(pStmt)==SQLITE_ROW; i++){ + int bPk = sqlite3_column_int(pStmt, 5); + int n = sqlite3_column_bytes(pStmt, 1); + const char *z = (const char*)sqlite3_column_text(pStmt, 1); + int eHidden = sqlite3_column_int(pStmt, 6); + + if( bPk ) pNew->iPk = i; + pNew->aCol[i].zCol = csr; + pNew->aCol[i].eHidden = eHidden; + memcpy(csr, z, n); + csr += (n+1); + } + + pNew->pNext = p->pTblList; + p->pTblList = pNew; + } + + recoverFinalize(p, pStmt); + + pStmt = recoverPreparePrintf(p, p->dbOut, "PRAGMA index_info(%Q)", zName); + if( pStmt && sqlite3_step(pStmt)!=SQLITE_ROW ){ + pNew->bIntkey = 1; + }else{ + pNew->iPk = -1; + } + recoverFinalize(p, pStmt); + } +} + +/* +** +*/ +static int recoverWriteSchema1(sqlite3_recover *p){ + sqlite3_stmt *pSelect = 0; + sqlite3_stmt *pTblname = 0; + + pSelect = recoverPrepare(p, p->dbOut, + "SELECT rootpage, sql, type='table' FROM recovery.schema " + " WHERE type='table' OR (type='index' AND sql LIKE '%unique%')" + ); + + pTblname = recoverPrepare(p, p->dbOut, + "SELECT name FROM sqlite_schema " + "WHERE type='table' ORDER BY rowid DESC LIMIT 1" + ); + + if( pSelect ){ + while( sqlite3_step(pSelect)==SQLITE_ROW ){ + i64 iRoot = sqlite3_column_int64(pSelect, 0); + const char *zSql = (const char*)sqlite3_column_text(pSelect, 1); + int bTable = sqlite3_column_int(pSelect, 2); + + int rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0); + if( rc==SQLITE_OK ){ + if( bTable ){ + if( SQLITE_ROW==sqlite3_step(pTblname) ){ + const char *zName = sqlite3_column_text(pTblname, 0); + recoverAddTable(p, zName, iRoot); + } + recoverReset(p, pTblname); + } + }else if( rc!=SQLITE_ERROR ){ + recoverDbError(p, p->dbOut); + } + } + } + recoverFinalize(p, pSelect); + recoverFinalize(p, pTblname); + + return p->errCode; +} + +static int recoverWriteSchema2(sqlite3_recover *p){ + sqlite3_stmt *pSelect = 0; + + pSelect = recoverPrepare(p, p->dbOut, + "SELECT rootpage, sql FROM recovery.schema " + " WHERE type!='table' AND (type!='index' OR sql NOT LIKE '%unique%')" + ); + + if( pSelect ){ + while( sqlite3_step(pSelect)==SQLITE_ROW ){ + i64 iRoot = sqlite3_column_int64(pSelect, 0); + const char *zSql = (const char*)sqlite3_column_text(pSelect, 1); + int rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0); + if( rc!=SQLITE_OK && rc!=SQLITE_ERROR ){ + recoverDbError(p, p->dbOut); + } + } + } + recoverFinalize(p, pSelect); + + return p->errCode; +} + + +static char *recoverMPrintf(sqlite3_recover *p, const char *zFmt, ...){ + char *zRet = 0; + if( p->errCode==SQLITE_OK ){ + va_list ap; + char *z; + va_start(ap, zFmt); + zRet = sqlite3_vmprintf(zFmt, ap); + va_end(ap); + if( zRet==0 ){ + p->errCode = SQLITE_NOMEM; + } + } + return zRet; +} + +static sqlite3_stmt *recoverInsertStmt( + sqlite3_recover *p, + RecoverTable *pTab, + int nField +){ + const char *zSep = ""; + char *zSql = 0; + char *zBind = 0; + int ii; + sqlite3_stmt *pRet = 0; + + assert( nField<=pTab->nCol ); + + zSql = recoverMPrintf(p, "INSERT OR IGNORE INTO %Q(", pTab->zTab); + for(ii=0; iiaCol[ii].eHidden; + if( eHidden!=RECOVER_EHIDDEN_VIRTUAL + && eHidden!=RECOVER_EHIDDEN_STORED + ){ + zSql = recoverMPrintf(p, "%z%s%Q", zSql, zSep, pTab->aCol[ii].zCol); + zBind = recoverMPrintf(p, "%z%s?", zBind, zSep); + zSep = ", "; + } + } + zSql = recoverMPrintf(p, "%z) VALUES (%z)", zSql, zBind); + + pRet = recoverPrepare(p, p->dbOut, zSql); + sqlite3_free(zSql); + + return pRet; +} + + +static RecoverTable *recoverFindTable(sqlite3_recover *p, u32 iRoot){ + RecoverTable *pRet = 0; + for(pRet=p->pTblList; pRet && pRet->iRoot!=iRoot; pRet=pRet->pNext); + return pRet; +} + +static int recoverWriteData(sqlite3_recover *p){ + RecoverTable *pTbl; + int nMax = 0; + sqlite3_value **apVal = 0; + sqlite3_stmt *pSel = 0; + + /* Figure out the maximum number of columns for any table in the schema */ + for(pTbl=p->pTblList; pTbl; pTbl=pTbl->pNext){ + if( pTbl->nCol>nMax ) nMax = pTbl->nCol; + } + + apVal = (sqlite3_value**)recoverMalloc(p, sizeof(sqlite3_value*) * nMax); + if( apVal==0 ) return p->errCode; + + pSel = recoverPrepare(p, p->dbOut, + "WITH RECURSIVE pages(root, page) AS (" + " SELECT rootpage, rootpage FROM recovery.schema" + " UNION" + " SELECT root, child FROM recovery.dbptr, pages WHERE pgno=page" + ") " + "SELECT root, page, cell, field, value " + "FROM sqlite_dbdata('getpage()') d, pages p WHERE p.page=d.pgno " + "UNION ALL " + "SELECT 0, 0, 0, 0, 0" + ); + if( pSel ){ + RecoverTable *pTab = 0; + sqlite3_stmt *pInsert = 0; + int nInsert = -1; + i64 iPrevRoot = -1; + i64 iPrevPage = -1; + int iPrevCell = -1; + i64 iRowid = 0; + int nVal = -1; + + while( p->errCode==SQLITE_OK && sqlite3_step(pSel)==SQLITE_ROW ){ + i64 iRoot = sqlite3_column_int64(pSel, 0); + i64 iPage = sqlite3_column_int64(pSel, 1); + int iCell = sqlite3_column_int(pSel, 2); + int iField = sqlite3_column_int(pSel, 3); + sqlite3_value *pVal = sqlite3_column_value(pSel, 4); + + int bNewCell = (iPrevRoot!=iRoot || iPrevPage!=iPage || iPrevCell!=iCell); + assert( bNewCell==0 || (iField==-1 || iField==0) ); + assert( bNewCell || iField==nVal ); + + if( bNewCell ){ + if( nVal>=0 ){ + int ii; + + if( pTab ){ + int iVal = 0; + int iBind = 1; + + if( pInsert==0 || nVal!=nInsert ){ + recoverFinalize(p, pInsert); + pInsert = recoverInsertStmt(p, pTab, nVal); + nInsert = nVal; + } + + for(ii=0; iinCol && iValaCol[ii].eHidden; + switch( eHidden ){ + case RECOVER_EHIDDEN_NONE: + case RECOVER_EHIDDEN_HIDDEN: + if( ii==pTab->iPk ){ + sqlite3_bind_int64(pInsert, iBind, iRowid); + }else{ + sqlite3_bind_value(pInsert, iBind, apVal[iVal]); + } + iBind++; + iVal++; + break; + + case RECOVER_EHIDDEN_VIRTUAL: + break; + + case RECOVER_EHIDDEN_STORED: + iVal++; + break; + } + } + + sqlite3_step(pInsert); + recoverReset(p, pInsert); + assert( p->errCode || pInsert ); + if( pInsert ) sqlite3_clear_bindings(pInsert); + } + + for(ii=0; iierrCode; +} + +sqlite3_recover *sqlite3_recover_init( + sqlite3* db, + const char *zDb, + const char *zUri +){ + sqlite3_recover *pRet = 0; + int nDb = 0; + int nUri = 0; + int nByte = 0; + + if( zDb==0 ){ zDb = "main"; } + if( zUri==0 ){ zUri = ""; } + + nDb = recoverStrlen(zDb); + nUri = recoverStrlen(zUri); + + nByte = sizeof(sqlite3_recover) + nDb+1 + nUri+1; + pRet = (sqlite3_recover*)sqlite3_malloc(nByte); + if( pRet ){ + memset(pRet, 0, nByte); + pRet->dbIn = db; + pRet->zDb = (char*)&pRet[1]; + pRet->zUri = &pRet->zDb[nDb+1]; + memcpy(pRet->zDb, zDb, nDb); + memcpy(pRet->zUri, zUri, nUri); + } + + return pRet; +} + +const char *sqlite3_recover_errmsg(sqlite3_recover *p){ + return p ? p->zErrMsg : "not an error"; +} +int sqlite3_recover_errcode(sqlite3_recover *p){ + return p ? p->errCode : SQLITE_NOMEM; +} + +int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){ + int rc = SQLITE_OK; + + switch( op ){ + case SQLITE_RECOVER_TESTDB: + sqlite3_free(p->zStateDb); + p->zStateDb = sqlite3_mprintf("%s", (char*)pArg); + break; + + default: + rc = SQLITE_NOTFOUND; + break; + } + + return rc; +} + +static void recoverStep(sqlite3_recover *p){ + + assert( p->errCode==SQLITE_OK ); + + if( p->dbOut==0 ){ + if( recoverOpenOutput(p) ) return; + if( recoverCacheDbptr(p) ) return; + if( recoverCacheSchema(p) ) return; + if( recoverWriteSchema1(p) ) return; + if( recoverWriteData(p) ) return; + if( recoverWriteSchema2(p) ) return; + } +} + +int sqlite3_recover_step(sqlite3_recover *p){ + if( p && p->errCode==SQLITE_OK ){ + recoverStep(p); + } + return p ? p->errCode : SQLITE_NOMEM; +} + +int sqlite3_recover_finish(sqlite3_recover *p){ + RecoverTable *pTab; + RecoverTable *pNext; + int rc; + + for(pTab=p->pTblList; pTab; pTab=pNext){ + pNext = pTab->pNext; + sqlite3_free(pTab); + } + + sqlite3_finalize(p->pGetPage); + rc = sqlite3_close(p->dbOut); + assert( rc==SQLITE_OK ); + p->pGetPage = 0; + rc = p->errCode; + + sqlite3_free(p); + return rc; +} + diff --git a/ext/recover/sqlite3recover.h b/ext/recover/sqlite3recover.h new file mode 100644 index 0000000000..401f83ea28 --- /dev/null +++ b/ext/recover/sqlite3recover.h @@ -0,0 +1,66 @@ +/* +** 2022-08-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. +** +************************************************************************* +** +*/ + + +#ifndef _SQLITE_RECOVER_H +#define _SQLITE_RECOVER_H + +#include "sqlite3.h" /* Required for error code definitions */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct sqlite3_recover sqlite3_recover; + +/* Create an object to recover data from database zDb (e.g. "main") +** opened by handle db. Data will be recovered into the database +** identified by parameter zUri. Database zUri is clobbered if it +** already exists. +*/ +sqlite3_recover *sqlite3_recover_init( + sqlite3* db, + const char *zDb, + const char *zUri +); + +/* Details TBD. */ +int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg); + +#define SQLITE_RECOVER_TESTDB 789 + +/* Step the recovery object. Return SQLITE_DONE if recovery is complete, +** SQLITE_OK if recovery is not complete but no error has occurred, or +** an SQLite error code if an error has occurred. +*/ +int sqlite3_recover_step(sqlite3_recover*); + +const char *sqlite3_recover_errmsg(sqlite3_recover*); + +int sqlite3_recover_errcode(sqlite3_recover*); + +/* Clean up a recovery object created by a call to sqlite3_recover_init(). +** This function returns SQLITE_DONE if the new database was created, +** SQLITE_OK if it processing was abandoned before it as finished or +** an SQLite error code (e.g. SQLITE_IOERR, SQLITE_NOMEM etc.) if an +** error occurred. */ +int sqlite3_recover_finish(sqlite3_recover*); + + +#ifdef __cplusplus +} /* end of the 'extern "C"' block */ +#endif + +#endif /* ifndef _SQLITE_RECOVER_H */ + diff --git a/ext/recover/test_recover.c b/ext/recover/test_recover.c new file mode 100644 index 0000000000..912b8dec5c --- /dev/null +++ b/ext/recover/test_recover.c @@ -0,0 +1,185 @@ +/* +** 2022-08-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. +** +************************************************************************* +** +*/ + +#include "sqlite3recover.h" + +#include +#include + +typedef struct TestRecover TestRecover; +struct TestRecover { + sqlite3_recover *p; +}; + +static int getDbPointer(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **pDb){ + Tcl_CmdInfo info; + if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(pObj), &info) ){ + Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(pObj), 0); + return TCL_ERROR; + } + *pDb = *(sqlite3 **)info.objClientData; + return TCL_OK; +} + +/* +** Implementation of the command created by [sqlite3_recover_init]: +** +** $cmd config OP ARG +** $cmd step +** $cmd errmsg +** $cmd errcode +** $cmd finalize +*/ +static int testRecoverCmd( + void *clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + static struct RecoverSub { + const char *zSub; + int nArg; + const char *zMsg; + } aSub[] = { + { "config", 2, "REBASE-BLOB" }, /* 0 */ + { "step", 0, "" }, /* 1 */ + { "errmsg", 0, "" }, /* 2 */ + { "errcode", 0, "" }, /* 3 */ + { "finish", 0, "" }, /* 4 */ + { 0 } + }; + int rc = TCL_OK; + int iSub = 0; + TestRecover *pTest = (TestRecover*)clientData; + + if( objc<2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ..."); + return TCL_ERROR; + } + rc = Tcl_GetIndexFromObjStruct(interp, + objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub + ); + if( rc!=TCL_OK ) return rc; + if( (objc-2)!=aSub[iSub].nArg ){ + Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg); + return TCL_ERROR; + } + + switch( iSub ){ + case 0: assert( sqlite3_stricmp("config", aSub[iSub].zSub)==0 ); { + const char *aOp[] = { + "testdb", /* 0 */ + 0 + }; + int iOp = 0; + int res = 0; + if( Tcl_GetIndexFromObj(interp, objv[2], aOp, "option", 0, &iOp) ){ + return TCL_ERROR; + } + switch( iOp ){ + case 0: + res = sqlite3_recover_config( + pTest->p, SQLITE_RECOVER_TESTDB, (void*)Tcl_GetString(objv[3]) + ); + break; + } + Tcl_SetObjResult(interp, Tcl_NewIntObj(res)); + break; + } + case 1: assert( sqlite3_stricmp("step", aSub[iSub].zSub)==0 ); { + int res = sqlite3_recover_step(pTest->p); + Tcl_SetObjResult(interp, Tcl_NewIntObj(res)); + break; + } + case 2: assert( sqlite3_stricmp("errmsg", aSub[iSub].zSub)==0 ); { + const char *zErr = sqlite3_recover_errmsg(pTest->p); + Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1)); + break; + } + case 3: assert( sqlite3_stricmp("errcode", aSub[iSub].zSub)==0 ); { + int errCode = sqlite3_recover_errcode(pTest->p); + Tcl_SetObjResult(interp, Tcl_NewIntObj(errCode)); + break; + } + case 4: assert( sqlite3_stricmp("finish", aSub[iSub].zSub)==0 ); { + int res = sqlite3_recover_errcode(pTest->p); + int res2; + if( res!=SQLITE_OK ){ + const char *zErr = sqlite3_recover_errmsg(pTest->p); + char *zRes = sqlite3_mprintf("(%d) - %s", res, zErr); + Tcl_SetObjResult(interp, Tcl_NewStringObj(zRes, -1)); + sqlite3_free(zRes); + } + res2 = sqlite3_recover_finish(pTest->p); + assert( res2==res ); + if( res ) return TCL_ERROR; + break; + } + } + + return TCL_OK; +} + +/* +** sqlite3_recover_init DB DBNAME URI +*/ +static int test_sqlite3_recover_init( + void *clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + static int iTestRecoverCmd = 1; + + TestRecover *pNew = 0; + sqlite3 *db = 0; + const char *zDb = 0; + const char *zUri = 0; + char zCmd[128]; + + if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME URI"); + return TCL_ERROR; + } + if( getDbPointer(interp, objv[1], &db) ) return TCL_ERROR; + zDb = Tcl_GetString(objv[2]); + zUri = Tcl_GetString(objv[3]); + + pNew = ckalloc(sizeof(TestRecover)); + pNew->p = sqlite3_recover_init(db, zDb, zUri); + + sprintf(zCmd, "sqlite_recover%d", iTestRecoverCmd++); + Tcl_CreateObjCommand(interp, zCmd, testRecoverCmd, (void*)pNew, 0); + + Tcl_SetObjResult(interp, Tcl_NewStringObj(zCmd, -1)); + return TCL_OK; +} + +int TestRecover_Init(Tcl_Interp *interp){ + struct Cmd { + const char *zCmd; + Tcl_ObjCmdProc *xProc; + } aCmd[] = { + { "sqlite3_recover_init", test_sqlite3_recover_init }, + }; + int i; + + for(i=0; izCmd, p->xProc, 0, 0); + } + + return TCL_OK; +} + diff --git a/main.mk b/main.mk index 3d8a07494d..5263546042 100644 --- a/main.mk +++ b/main.mk @@ -444,6 +444,9 @@ TESTSRC2 = \ $(TOP)/ext/misc/stmt.c \ $(TOP)/ext/session/sqlite3session.c \ $(TOP)/ext/session/test_session.c \ + $(TOP)/ext/recover/sqlite3recover.c \ + $(TOP)/ext/misc/dbdata.c \ + $(TOP)/ext/recover/test_recover.c \ fts5.c # Header files used by all library source files. diff --git a/manifest b/manifest index 746bbe3c38..0fab53a46f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enhance\sthe\sb-tree\spage\ssorting\scode\sto\sensure\sthat\ssqlite3PagerRekey()\snever\noverloads\sa\spage\snumber\sand\suses\sonly\sthe\sPENDING_BYTE\spage\sfor\stemporary\nstorage. -D 2022-08-31T15:04:42.204 +C Add\snew\sfiles\sfor\san\sextension\sto\srecover\sdata\sfrom\scorrupted\sdatabases. +D 2022-08-31T20:45:43.730 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -299,7 +299,7 @@ F ext/misc/closure.c dbfd8543b2a017ae6b1a5843986b22ddf99ff126ec9634a2f4047cd14c8 F ext/misc/completion.c 6dafd7f4348eecc7be9e920d4b419d1fb2af75d938cd9c59a20cfe8beb2f22b9 F ext/misc/compress.c 3354c77a7c8e86e07d849916000cdac451ed96500bfb5bd83b20eb61eee012c9 F ext/misc/csv.c ca8d6dafc5469639de81937cb66ae2e6b358542aba94c4f791910d355a8e7f73 -F ext/misc/dbdata.c e316fba936571584e55abd5b974a32a191727a6b746053a0c9d439bd2cf93940 +F ext/misc/dbdata.c f317980cea788e67932828b94a16ee8a8b859e3c2d62859d09ba3d5ca85f87cb F ext/misc/dbdump.c b8592f6f2da292c62991a13864a60d6c573c47a9cc58362131b9e6a64f823e01 F ext/misc/decimal.c 09f967dcf4a1ee35a76309829308ec278d3648168733f4a1147820e11ebefd12 F ext/misc/eval.c 04bc9aada78c888394204b4ed996ab834b99726fb59603b0ee3ed6e049755dc1 @@ -387,6 +387,11 @@ F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2 F ext/rbu/sqlite3rbu.c 8737cabdfbee84bb25a7851ecef8b1312be332761238da9be6ddb10c62ad4291 F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812 F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a +F ext/recover/recover1.test 861ad5140566102a8c5a3d1f936a7d6da569f34c86597c274de695f597031bac +F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c +F ext/recover/sqlite3recover.c 594fb45777a14f0b88b944b9fb2ccb3e85a29ef5b17522b8dac3e3944c4c27ea +F ext/recover/sqlite3recover.h 3255f6491007e57be310aedb72a848c88f79fc14e7222bda4b8d4dab1a2450c3 +F ext/recover/test_recover.c 919f61df54776598b350250057fd2d3ea9cc2cef1aeac0dbb760958d26fe1afb F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 F ext/repair/checkfreelist.c e21f06995ff4efdc1622dcceaea4dcba2caa83ca2f31a1607b98a8509168a996 F ext/repair/checkindex.c 4383e4469c21e5b9ae321d0d63cec53e981af9d7a6564be6374f0eeb93dfc890 @@ -509,7 +514,7 @@ F ext/wasm/testing2.js d37433c601f88ed275712c1cfc92d3fb36c7c22e1ed8c7396fb2359e4 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 20801eed419dc58936ff9449b04041edbbbc0488a9fc683e72471dded050e0bb +F main.mk 8c9965c408aaa8b93d0dd52e83445894835e1a42dc360c77435393f80f8d8d1d F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -641,7 +646,7 @@ F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe F src/test_sqllog.c 540feaea7280cd5f926168aee9deb1065ae136d0bbbe7361e2ef3541783e187a F src/test_superlock.c 4839644b9201da822f181c5bc406c0b2385f672e F src/test_syscall.c 1073306ba2e9bfc886771871a13d3de281ed3939 -F src/test_tclsh.c c4065ced25126e25c40122c5ff62dc89902ea617d72cdd27765151cdd7fcc477 +F src/test_tclsh.c 7dd98be675a1dc0d1fd302b8247bab992c909db384df054381a2279ad76f9b0e F src/test_tclvar.c 33ff42149494a39c5fbb0df3d25d6fafb2f668888e41c0688d07273dcb268dfc F src/test_thread.c 269ea9e1fa5828dba550eb26f619aa18aedbc29fd92f8a5f6b93521fbb74a61c F src/test_vdbecov.c f60c6f135ec42c0de013a1d5136777aa328a776d33277f92abac648930453d43 @@ -1999,8 +2004,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P dd017bb1b3e31c7692d29dc4865d6bda871e429978c8738a39160d0114e5bf9b -R 98122ff0aaf5ca87deda59c5c8a25251 -U drh -Z 8d73d18db9ab73a94a9689d17f937c1d +P 5007742886bd20de20be3973737cf46b010359911615eb3da69cd262bd9a2435 +R 563b8320bf923831e4768bc403655fc2 +T *branch * recover-extension +T *sym-recover-extension * +T -sym-trunk * +U dan +Z 1c7612740eb933f84d589533d182c6df # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 00cb077051..16703e1b2f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5007742886bd20de20be3973737cf46b010359911615eb3da69cd262bd9a2435 \ No newline at end of file +f8298eeba01cb5b02ac4d642c06f3801331ca90edea533ea898a3283981a9e49 \ No newline at end of file diff --git a/src/test_tclsh.c b/src/test_tclsh.c index 707c16812c..c133deca25 100644 --- a/src/test_tclsh.c +++ b/src/test_tclsh.c @@ -108,6 +108,7 @@ const char *sqlite3TestInit(Tcl_Interp *interp){ extern int TestExpert_Init(Tcl_Interp*); extern int Sqlitetest_window_Init(Tcl_Interp *); extern int Sqlitetestvdbecov_Init(Tcl_Interp *); + extern int TestRecover_Init(Tcl_Interp*); Tcl_CmdInfo cmdInfo; @@ -175,6 +176,7 @@ const char *sqlite3TestInit(Tcl_Interp *interp){ TestExpert_Init(interp); Sqlitetest_window_Init(interp); Sqlitetestvdbecov_Init(interp); + TestRecover_Init(interp); Tcl_CreateObjCommand( interp, "load_testfixture_extensions", load_testfixture_extensions,0,0 From 6c86783f9a212781f6e69dbf102c1048e1c066f4 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 1 Sep 2022 21:00:39 +0000 Subject: [PATCH 043/428] Further work on making recovery extension compatible with the shell tool ".recover" code. FossilOrigin-Name: 8df7c7d0dcd1b2fcdad00e765a9868407f0ced02ac4432ee2cdf25d83b130759 --- ext/recover/recover1.test | 2 + ext/recover/sqlite3recover.c | 177 +++++++++++++++++++++++++++++++---- ext/recover/sqlite3recover.h | 3 +- manifest | 19 ++-- manifest.uuid | 2 +- 5 files changed, 171 insertions(+), 32 deletions(-) diff --git a/ext/recover/recover1.test b/ext/recover/recover1.test index c4348baea0..73832c8a12 100644 --- a/ext/recover/recover1.test +++ b/ext/recover/recover1.test @@ -40,9 +40,11 @@ proc compare_dbs {db1 db2} { proc do_recover_test {tn} { forcedelete test.db2 + forcedelete rstate.db uplevel [list do_test $tn.1 { set R [sqlite3_recover_init db main test.db2] + $R config testdb rstate.db $R step $R finish } {}] diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index 6c7828c007..d8b151616b 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -48,6 +48,12 @@ struct RecoverTable { RecoverTable *pNext; }; +typedef struct RecoverBitmap RecoverBitmap; +struct RecoverBitmap { + i64 nPg; /* Size of bitmap */ + u32 aElem[0]; /* Array of 32-bit bitmasks */ +}; + /* ** */ @@ -59,7 +65,7 @@ struct RecoverTable { " pgno, child, PRIMARY KEY(child, pgno)" \ " ) WITHOUT ROWID;" \ " CREATE TABLE recovery.map(" \ -" pgno INTEGER PRIMARY KEY, maxlen INT, intkey, root INT" \ +" pgno INTEGER PRIMARY KEY, parent INT" \ " );" \ " CREATE TABLE recovery.schema(" \ " type, name, tbl_name, rootpage, sql" \ @@ -75,11 +81,14 @@ struct sqlite3_recover { char *zDb; char *zUri; RecoverTable *pTblList; + RecoverBitmap *pUsed; /* Used by recoverWriteLostAndFound() */ int errCode; /* For sqlite3_recover_errcode() */ char *zErrMsg; /* For sqlite3_recover_errmsg() */ char *zStateDb; + int bLostAndFound; + }; /* @@ -124,6 +133,41 @@ static int recoverError( return errCode; } + +static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){ + int nElem = (nPg+1+31) / 32; + int nByte = sizeof(RecoverBitmap) + nElem*sizeof(u32); + RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte); + + if( pRet ){ + pRet->nPg = nPg; + } + return pRet; +} + +static void recoverBitmapFree(RecoverBitmap *pMap){ + sqlite3_free(pMap); +} + +static void recoverBitmapSet(RecoverBitmap *pMap, i64 iPg){ + if( iPg<=pMap->nPg ){ + int iElem = (iPg / 32); + int iBit = (iPg % 32); + pMap->aElem[iElem] |= (((u32)1) << iBit); + } +} + +static int recoverBitmapQuery(RecoverBitmap *pMap, i64 iPg){ + int ret = 1; + if( iPg<=pMap->nPg ){ + int iElem = (iPg / 32); + int iBit = (iPg % 32); + ret = (pMap->aElem[iElem] & (((u32)1) << iBit)) ? 1 : 0; + } + return ret; +} + + static int recoverDbError(sqlite3_recover *p, sqlite3 *db){ return recoverError(p, sqlite3_errcode(db), "%s", sqlite3_errmsg(db)); } @@ -194,6 +238,24 @@ static int recoverExec(sqlite3_recover *p, sqlite3 *db, const char *zSql){ return p->errCode; } +/* +** Execute "PRAGMA page_count" against the input database. If successful, +** return the integer result. Or, if an error occurs, leave an error code +** and error message in the sqlite3_recover handle. +*/ +static i64 recoverPageCount(sqlite3_recover *p){ + i64 nPg = 0; + if( p->errCode==SQLITE_OK ){ + sqlite3_stmt *pStmt = 0; + pStmt = recoverPreparePrintf(p, p->dbIn, "PRAGMA %Q.page_count", p->zDb); + if( pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){ + nPg = sqlite3_column_int64(pStmt, 0); + } + recoverFinalize(p, pStmt); + } + return nPg; +} + /* ** The implementation of a user-defined SQL function invoked by the ** sqlite_dbdata and sqlite_dbptr virtual table modules to access pages @@ -219,24 +281,23 @@ static void recoverGetPage( assert( nArg==1 ); if( pgno==0 ){ - pStmt = recoverPreparePrintf(p, p->dbIn, "PRAGMA %Q.page_count", p->zDb); - }else if( p->pGetPage==0 ){ - pStmt = recoverPreparePrintf( - p, p->dbIn, "SELECT data FROM sqlite_dbpage(%Q) WHERE pgno=?", p->zDb - ); + sqlite3_result_int64(pCtx, recoverPageCount(p)); + return; }else{ - pStmt = p->pGetPage; - } - - if( pStmt ){ - if( pgno ) sqlite3_bind_int64(pStmt, 1, pgno); - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - sqlite3_result_value(pCtx, sqlite3_column_value(pStmt, 0)); - } - if( pgno ){ - p->pGetPage = recoverReset(p, pStmt); + if( p->pGetPage==0 ){ + pStmt = recoverPreparePrintf( + p, p->dbIn, "SELECT data FROM sqlite_dbpage(%Q) WHERE pgno=?", p->zDb + ); }else{ - recoverFinalize(p, pStmt); + pStmt = p->pGetPage; + } + + if( pStmt ){ + sqlite3_bind_int64(pStmt, 1, pgno); + if( SQLITE_ROW==sqlite3_step(pStmt) ){ + sqlite3_result_value(pCtx, sqlite3_column_value(pStmt, 0)); + } + p->pGetPage = recoverReset(p, pStmt); } } @@ -311,7 +372,7 @@ static int recoverCacheSchema(sqlite3_recover *p){ "WITH RECURSIVE pages(p) AS (" " SELECT 1" " UNION" - " SELECT child FROM recovery.dbptr, pages WHERE pgno=p" + " SELECT child FROM sqlite_dbptr('getpage()'), pages WHERE pgno=p" ")" "INSERT INTO recovery.schema SELECT" " max(CASE WHEN field=0 THEN value ELSE NULL END)," @@ -504,6 +565,77 @@ static RecoverTable *recoverFindTable(sqlite3_recover *p, u32 iRoot){ return pRet; } +static int recoverWriteLostAndFound(sqlite3_recover *p){ + i64 nPg = 0; + RecoverBitmap *pMap = 0; + + nPg = recoverPageCount(p); + pMap = p->pUsed = recoverBitmapAlloc(p, nPg); + if( pMap ){ + sqlite3_stmt *pStmt = 0; + char *zField = 0; + const char *zSep = 0; + int ii; + + sqlite3_stmt *pStmt = recoverPrepare( + p, p->dbOut, + "WITH RECURSIVE used(page) AS (" + " SELECT rootpage FROM recovery.schema WHERE rootpage>0" + " UNION" + " SELECT child FROM sqlite_dbptr('getpage()'), used " + " WHERE pgno=page" + ") " + "SELECT page FROM used" + ); + while( pStmt && sqlite3_step(pStmt) ){ + i64 iPg = sqlite3_column_int64(pStmt); + recoverBitmapSet(pMap, iPg); + } + recoverFinalize(pStmt); + + pStmt = recoverPreparePrintf( + p, p->dbOut, + "WITH RECURSIVE seq(ii) AS (" + " SELECT 1 UNION ALL SELECT ii+1 FROM seq WHERE ii<%lld" + ")" + "INSERT INTO recover.map(pgno) " + " SELECT ii FROM seq WHERE !page_is_used(ii)" + ); + sqlite3_step(pStmt); + recoverFinalize(pStmt); + + pStmt = recoverPrepare( + p, p->dbOut, + "UPDATE recover.map SET parent = ptr.pgno " + " FROM sqlite_dbptr('getpage()') WHERE recover.map.pgno=ptr.child" + ); + sqlite3_step(pStmt); + recoverFinalize(pStmt); + + pStmt = recoverPrepare( + p, p->dbOut, + "SELECT max(field) FROM sqlite_dbdata('getpage') WHERE pgno IN (" + " SELECT pgno FROM recover.map" + ")" + ); + if( pStmt && sqlite3_step(pStmt) ){ + nMaxField = sqlite3_column_int64(pStmt, 0); + } + recoverFinalize(pStmt); + + if( nMaxField==0 || p->errCode!=SQLITE_OK ) return p->errCode; + + zSep = "rootpgno INTEGER, pgno INTEGER, nfield INTEGER, id INTEGER, "; + for(ii=0; iierrCode = SQLITE_NOMEM; + } + } + } +} + static int recoverWriteData(sqlite3_recover *p){ RecoverTable *pTbl; int nMax = 0; @@ -522,7 +654,8 @@ static int recoverWriteData(sqlite3_recover *p){ "WITH RECURSIVE pages(root, page) AS (" " SELECT rootpage, rootpage FROM recovery.schema" " UNION" - " SELECT root, child FROM recovery.dbptr, pages WHERE pgno=page" + " SELECT root, child FROM sqlite_dbptr('getpage()'), pages " + " WHERE pgno=page" ") " "SELECT root, page, cell, field, value " "FROM sqlite_dbdata('getpage()') d, pages p WHERE p.page=d.pgno " @@ -677,6 +810,10 @@ int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){ p->zStateDb = sqlite3_mprintf("%s", (char*)pArg); break; + case SQLITE_RECOVER_LOST_AND_FOUND: + p->bLostAndFound = (pArg ? 1 : 0); + break; + default: rc = SQLITE_NOTFOUND; break; @@ -695,6 +832,7 @@ static void recoverStep(sqlite3_recover *p){ if( recoverCacheSchema(p) ) return; if( recoverWriteSchema1(p) ) return; if( recoverWriteData(p) ) return; + if( p->bLostAndFound && recoverWriteLostAndFound(p) ) return; if( recoverWriteSchema2(p) ) return; } } @@ -722,6 +860,7 @@ int sqlite3_recover_finish(sqlite3_recover *p){ p->pGetPage = 0; rc = p->errCode; + sqlite3_free(p->zStateDb); sqlite3_free(p); return rc; } diff --git a/ext/recover/sqlite3recover.h b/ext/recover/sqlite3recover.h index 401f83ea28..0c83f8dea5 100644 --- a/ext/recover/sqlite3recover.h +++ b/ext/recover/sqlite3recover.h @@ -38,7 +38,8 @@ sqlite3_recover *sqlite3_recover_init( /* Details TBD. */ int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg); -#define SQLITE_RECOVER_TESTDB 789 +#define SQLITE_RECOVER_TESTDB 789 +#define SQLITE_RECOVER_LOST_AND_FOUND 790 /* Step the recovery object. Return SQLITE_DONE if recovery is complete, ** SQLITE_OK if recovery is not complete but no error has occurred, or diff --git a/manifest b/manifest index 0fab53a46f..7f8f333f25 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\snew\sfiles\sfor\san\sextension\sto\srecover\sdata\sfrom\scorrupted\sdatabases. -D 2022-08-31T20:45:43.730 +C Further\swork\son\smaking\srecovery\sextension\scompatible\swith\sthe\sshell\stool\s".recover"\scode. +D 2022-09-01T21:00:39.747 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -387,10 +387,10 @@ F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2 F ext/rbu/sqlite3rbu.c 8737cabdfbee84bb25a7851ecef8b1312be332761238da9be6ddb10c62ad4291 F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812 F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a -F ext/recover/recover1.test 861ad5140566102a8c5a3d1f936a7d6da569f34c86597c274de695f597031bac +F ext/recover/recover1.test a848af8c82fe0731af835ff99475724f8654d2f24f772cc4e6f7ec4eb2ab71ea F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c -F ext/recover/sqlite3recover.c 594fb45777a14f0b88b944b9fb2ccb3e85a29ef5b17522b8dac3e3944c4c27ea -F ext/recover/sqlite3recover.h 3255f6491007e57be310aedb72a848c88f79fc14e7222bda4b8d4dab1a2450c3 +F ext/recover/sqlite3recover.c d81b430f968d838035ebf5ca168b43ae8a0bec1e7c2c950b74ec4fd5e16ca47b +F ext/recover/sqlite3recover.h 94e277a9b314a03df46b5e94cc44b70ed6c6893d2776d09c7ea0b55c969ad854 F ext/recover/test_recover.c 919f61df54776598b350250057fd2d3ea9cc2cef1aeac0dbb760958d26fe1afb F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 F ext/repair/checkfreelist.c e21f06995ff4efdc1622dcceaea4dcba2caa83ca2f31a1607b98a8509168a996 @@ -2004,11 +2004,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 5007742886bd20de20be3973737cf46b010359911615eb3da69cd262bd9a2435 -R 563b8320bf923831e4768bc403655fc2 -T *branch * recover-extension -T *sym-recover-extension * -T -sym-trunk * +P f8298eeba01cb5b02ac4d642c06f3801331ca90edea533ea898a3283981a9e49 +R 185cdcc7bc591773e93e55f9de4bfb4c U dan -Z 1c7612740eb933f84d589533d182c6df +Z 5d96e275b1b3f6a2d23b93e2a033e9c9 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 16703e1b2f..5c5b60080f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f8298eeba01cb5b02ac4d642c06f3801331ca90edea533ea898a3283981a9e49 \ No newline at end of file +8df7c7d0dcd1b2fcdad00e765a9868407f0ced02ac4432ee2cdf25d83b130759 \ No newline at end of file From cdefd5d046db3baa83e18b82cac15c54c8e8d60a Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 3 Sep 2022 11:41:44 +0000 Subject: [PATCH 044/428] wasm: minor text and build cleanups. FossilOrigin-Name: 36ceef94e1935f5e85f79e489cd0ed265a77820fb68329c190794df5e076bf84 --- ext/wasm/GNUmakefile | 12 ++++++++++-- ext/wasm/batch-runner.js | 11 +++++------ ext/wasm/common/SqliteTestUtil.js | 13 +++++++++---- manifest | 16 ++++++++-------- manifest.uuid | 2 +- 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 39cf8b26e4..11b1402662 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -167,9 +167,12 @@ $(post-js.js): $(post-jses) $(MAKEFILE) ######################################################################## -# emcc flags for .c/.o/.wasm. +# emcc flags for .c/.o/.wasm/.js. emcc.flags = #emcc.flags += -v # _very_ loud but also informative about what it's doing +# -g is needed to keep -O2 and higher from creating broken JS via +# minification. +emcc.flags += -g ######################################################################## # emcc flags for .c/.o. @@ -181,6 +184,7 @@ emcc.cflags += -I. -I$(dir.top) # $(SQLITE_OPT) ######################################################################## # emcc flags specific to building the final .js/.wasm file... emcc.jsflags := -fPIC +emcc.jsflags := --minify 0 emcc.jsflags += --no-entry emcc.jsflags += -sMODULARIZE emcc.jsflags += -sSTRICT_JS @@ -303,7 +307,11 @@ $(speedtest1.sql): $(speedtest1) batch-runner.list: $(MAKEFILE) $(speedtest1.sql) $(dir.sql)/000-mandelbrot.sql bash split-speedtest1-script.sh $(dir.sql)/speedtest1.sql ls -1 $(dir.sql)/*.sql | grep -v speedtest1.sql | sort > $@ -CLEAN_FILES += batch-runner.list $(dir.sql)/speedtest1*.sql +clean-batch: + rm -f batch-runner.list $(dir.sql)/speedtest1*.sql +# ^^^ we don't do this along with 'clean' because we clean/rebuild on +# a regular basis with different -Ox flags and rebuilding the batch +# pieces each time is an unnecessary time sink. batch: batch-runner.list all: batch diff --git a/ext/wasm/batch-runner.js b/ext/wasm/batch-runner.js index 909a140047..6cee157051 100644 --- a/ext/wasm/batch-runner.js +++ b/ext/wasm/batch-runner.js @@ -10,12 +10,11 @@ *********************************************************************** - A basic batch SQL running for sqlite3-api.js. This file must be run in + A basic batch SQL runner for sqlite3-api.js. This file must be run in main JS thread and sqlite3.js must have been loaded before it. */ 'use strict'; (function(){ - const T = self.SqliteTestUtil; const toss = function(...args){throw new Error(args.join(' '))}; const warn = console.warn.bind(console); @@ -218,7 +217,7 @@ const url = URL.createObjectURL(b); const a = document.createElement('a'); a.href = url; - a.download = 'batch-runner-metrics-'+((new Date().getTime()/1000) | 0)+'.csv'; + a.download = 'batch-runner-js-'+((new Date().getTime()/1000) | 0)+'.csv'; this.logHtml("Triggering download of",a.download); document.body.appendChild(a); a.click(); @@ -376,10 +375,10 @@ } }, false); this.e.btnExportMetrics.addEventListener('click', function(){ - who.logHtml2('warning',"Metrics export is a Work in Progress. See output in dev console."); + who.logHtml2('warning',"Triggering download of metrics CSV. Check your downloads folder."); who.downloadMetrics(); - const m = who.metricsToArrays(); - console.log("Metrics:",who.metrics, m); + //const m = who.metricsToArrays(); + //console.log("Metrics:",who.metrics, m); }); this.e.btnRunRemaining.addEventListener('click', async function(){ let v = who.e.selSql.value; diff --git a/ext/wasm/common/SqliteTestUtil.js b/ext/wasm/common/SqliteTestUtil.js index c21469c9b0..51cd64108e 100644 --- a/ext/wasm/common/SqliteTestUtil.js +++ b/ext/wasm/common/SqliteTestUtil.js @@ -122,6 +122,11 @@ sqlite3InitModule() factory function. */ self.sqlite3TestModule = { + /** + Array of functions to call after Emscripten has initialized the + wasm module. Each gets passed the Emscripten module object + (which is _this_ object). + */ postRun: [ /* function(theModule){...} */ ], @@ -135,10 +140,10 @@ 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). + Called by the Emscripten 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){ diff --git a/manifest b/manifest index d6ecf92b24..ddfd3bdce4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C batch-runner.js:\sadd\srudimentary\smetrics\sexport\sto\sCSV.\sAdd\sa\stoggle\sto\sreverse\sthe\slog\soutput\smode\s(in\snormal\sorder\sor\smost\srecent\sfirst). -D 2022-08-30T17:57:50.778 +C wasm:\sminor\stext\sand\sbuild\scleanups. +D 2022-09-03T11:41:44.176 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile fd1ba419c38d79f742849cc96a1f1e1dd63821e6173bc5727f0a5439f04b4131 +F ext/wasm/GNUmakefile 49d76f7b4c45d6ff8cb573ba0d1dd7b4712e59582bdc1dfb2f96a4b89f47e34c F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -490,8 +490,8 @@ F ext/wasm/api/sqlite3-api-worker1.js 73579555563b789785ae83724014eaf31811073aad F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 F ext/wasm/batch-runner.html 439245594711981831f2775f1426fd20d73a9b1bbf53f2cbbfc1194fa069891c -F ext/wasm/batch-runner.js dfe194ff87dac20e641aeee7080b74a877ca4a24ea107d6a80ff8115b28b7f0b -F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247 +F ext/wasm/batch-runner.js a727cbbffe63fd17fb5a590dc679f0b13bd51880e8f84b461d7df246417689e8 +F ext/wasm/common/SqliteTestUtil.js 7a543e238c2ebda922c85076abda017d0480944fdfee576692a0c3a580319ebd F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 F ext/wasm/common/whwasmutil.js 3d68a9cd87a2d8f7e6d5a78f3b829a927f2e28422685e029139f6e75d4ed42e4 @@ -2013,8 +2013,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P ba08f7a7f12601aa580a8dc493b43cdd5ab65c87d5e4b3fd2f549343e45c2d2a -R cd46865d209bc499b87d03a53afeb5f7 +P b26e2bc05537a1f451db1bdd36a824d71c3ea25a130b6285f31ff9799d65fa7a +R be49009f54e0ae14227cc16ba0e36553 U stephan -Z a638c92d95e9388486f91bc003769b2d +Z eee0918a1e799dd377ca7c8eb624a8df # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9cdf828810..99f4bce223 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b26e2bc05537a1f451db1bdd36a824d71c3ea25a130b6285f31ff9799d65fa7a \ No newline at end of file +36ceef94e1935f5e85f79e489cd0ed265a77820fb68329c190794df5e076bf84 \ No newline at end of file From f2f8a3a348c59d1faabaf9690422fab09002ae32 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 3 Sep 2022 20:07:39 +0000 Subject: [PATCH 045/428] Further work on making the recover extension compatible with the .recover command. FossilOrigin-Name: f2ac315844d8db1bd1c6950a4fef7c459ddd37cc21a8f3daafa5639fad8118e2 --- ext/misc/dbdata.c | 1 + ext/recover/recoverold.test | 139 ++++++++++++ ext/recover/sqlite3recover.c | 396 +++++++++++++++++++++++++++++------ ext/recover/sqlite3recover.h | 12 ++ ext/recover/test_recover.c | 12 +- manifest | 21 +- manifest.uuid | 2 +- test/permutations.test | 1 + 8 files changed, 503 insertions(+), 81 deletions(-) create mode 100644 ext/recover/recoverold.test diff --git a/ext/misc/dbdata.c b/ext/misc/dbdata.c index b79eafce7c..a18304b96b 100644 --- a/ext/misc/dbdata.c +++ b/ext/misc/dbdata.c @@ -477,6 +477,7 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){ rc = dbdataLoadPage(pCsr, pCsr->iPgno, &pCsr->aPage, &pCsr->nPage); if( rc!=SQLITE_OK ) return rc; if( pCsr->aPage ) break; + if( pCsr->bOnePage ) return SQLITE_OK; pCsr->iPgno++; } pCsr->iCell = pTab->bPtr ? -2 : 0; diff --git a/ext/recover/recoverold.test b/ext/recover/recoverold.test new file mode 100644 index 0000000000..61f09397b8 --- /dev/null +++ b/ext/recover/recoverold.test @@ -0,0 +1,139 @@ +# 2019 April 23 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] recover_common.tcl] +source $testdir/tester.tcl +set testprefix recoverold + +ifcapable !vtab { + finish_test; return +} + +proc compare_result {db1 db2 sql} { + set r1 [$db1 eval $sql] + set r2 [$db2 eval $sql] + if {$r1 != $r2} { + puts "sql: $sql" + puts "r1: $r1" + puts "r2: $r2" + error "mismatch for $sql" + } + return "" +} + +proc compare_dbs {db1 db2} { + compare_result $db1 $db2 "SELECT sql FROM sqlite_master ORDER BY 1" + foreach tbl [$db1 eval {SELECT name FROM sqlite_master WHERE type='table'}] { + compare_result $db1 $db2 "SELECT * FROM $tbl" + } +} + +proc do_recover_test {tn {tsql {}} {res {}}} { + forcedelete test.db2 + forcedelete rstate.db + + set R [sqlite3_recover_init db main test.db2] + $R config lostandfound lost_and_found + $R config testdb rstate.db + $R step + $R finish + + sqlite3 db2 test.db2 + + if {$tsql==""} { + uplevel [list do_test $tn [list compare_dbs db db2] {}] + } else { + uplevel [list do_execsql_test -db db2 $tn $tsql $res] + } + db2 close +} + +set doc { + hello + world +} +do_execsql_test 1.1.1 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); + INSERT INTO t1 VALUES(1, 4, X'1234567800'); + INSERT INTO t1 VALUES(2, 'test', 8.1); + INSERT INTO t1 VALUES(3, $doc, 8.4); +} +do_recover_test 1.1.2 + +do_execsql_test 1.2.1 " + DELETE FROM t1; + INSERT INTO t1 VALUES(13, 'hello\r\nworld', 13); +" +do_recover_test 1.2.2 + +do_execsql_test 1.3.1 " + CREATE TABLE t2(i INTEGER PRIMARY KEY AUTOINCREMENT, b, c); + INSERT INTO t2 VALUES(NULL, 1, 2); + INSERT INTO t2 VALUES(NULL, 3, 4); + INSERT INTO t2 VALUES(NULL, 5, 6); + CREATE TABLE t3(i INTEGER PRIMARY KEY AUTOINCREMENT, b, c); + INSERT INTO t3 VALUES(NULL, 1, 2); + INSERT INTO t3 VALUES(NULL, 3, 4); + INSERT INTO t3 VALUES(NULL, 5, 6); + DELETE FROM t2; +" +do_recover_test 1.3.2 + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 2.1.0 { + PRAGMA auto_vacuum = 0; + CREATE TABLE t1(a, b, c, PRIMARY KEY(b, c)) WITHOUT ROWID; + INSERT INTO t1 VALUES(1, 2, 3); + INSERT INTO t1 VALUES(4, 5, 6); + INSERT INTO t1 VALUES(7, 8, 9); +} + +do_recover_test 2.1.1 + +do_execsql_test 2.2.0 { + PRAGMA writable_schema = 1; + DELETE FROM sqlite_master WHERE name='t1'; +} +do_recover_test 2.2.1 { + SELECT name FROM sqlite_master +} {lost_and_found} + +do_execsql_test 2.3.0 { + CREATE TABLE lost_and_found(a, b, c); +} +do_recover_test 2.3.1 { + SELECT name FROM sqlite_master +} {lost_and_found lost_and_found_0} + +do_execsql_test 2.4.0 { + CREATE TABLE lost_and_found_0(a, b, c); +} +do_recover_test 2.4.1 { + SELECT name FROM sqlite_master; + SELECT * FROM lost_and_found_1; +} {lost_and_found lost_and_found_0 lost_and_found_1 + 2 2 3 {} 2 3 1 + 2 2 3 {} 5 6 4 + 2 2 3 {} 8 9 7 +} + +#------------------------------------------------------------------------- +breakpoint +reset_db +do_recover_test 3.0 + +finish_test diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index d8b151616b..ec333f0d05 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -22,6 +22,9 @@ typedef sqlite3_int64 i64; typedef struct RecoverColumn RecoverColumn; struct RecoverColumn { + int iField; /* Field in record on disk */ + int iBind; /* Binding to use in INSERT */ + int bIPK; /* True for IPK column */ char *zCol; int eHidden; }; @@ -35,6 +38,9 @@ struct RecoverColumn { ** When running the ".recover" command, each output table, and the special ** orphaned row table if it is required, is represented by an instance ** of the following struct. +** +** aCol[]: +** Array of nCol columns. In the order in which they appear in the table. */ typedef struct RecoverTable RecoverTable; struct RecoverTable { @@ -43,7 +49,6 @@ struct RecoverTable { int nCol; /* Number of columns in table */ RecoverColumn *aCol; /* Array of columns */ int bIntkey; /* True for intkey, false for without rowid */ - int iPk; /* Index of IPK column, if bIntkey */ RecoverTable *pNext; }; @@ -81,13 +86,13 @@ struct sqlite3_recover { char *zDb; char *zUri; RecoverTable *pTblList; - RecoverBitmap *pUsed; /* Used by recoverWriteLostAndFound() */ + RecoverBitmap *pUsed; /* Used by recoverLostAndFound() */ int errCode; /* For sqlite3_recover_errcode() */ char *zErrMsg; /* For sqlite3_recover_errmsg() */ char *zStateDb; - int bLostAndFound; + char *zLostAndFound; /* Name of lost-and-found table (or NULL) */ }; @@ -238,6 +243,21 @@ static int recoverExec(sqlite3_recover *p, sqlite3 *db, const char *zSql){ return p->errCode; } +static char *recoverPrintf(sqlite3_recover *p, const char *zFmt, ...){ + va_list ap; + char *z; + va_start(ap, zFmt); + z = sqlite3_vmprintf(zFmt, ap); + va_end(ap); + if( p->errCode==SQLITE_OK ){ + if( z==0 ) p->errCode = SQLITE_NOMEM; + }else{ + sqlite3_free(z); + z = 0; + } + return z; +} + /* ** Execute "PRAGMA page_count" against the input database. If successful, ** return the integer result. Or, if an error occurs, leave an error code @@ -256,6 +276,24 @@ static i64 recoverPageCount(sqlite3_recover *p){ return nPg; } +/* +** SELECT page_is_used(pgno); +*/ +static void recoverPageIsUsed( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ + sqlite3_recover *p = (sqlite3_recover*)sqlite3_user_data(pCtx); + sqlite3_int64 pgno = sqlite3_value_int64(apArg[0]); + sqlite3_stmt *pStmt = 0; + int bRet; + + assert( nArg==1 ); + bRet = recoverBitmapQuery(p->pUsed, pgno); + sqlite3_result_int(pCtx, bRet); +} + /* ** The implementation of a user-defined SQL function invoked by the ** sqlite_dbdata and sqlite_dbptr virtual table modules to access pages @@ -281,13 +319,14 @@ static void recoverGetPage( assert( nArg==1 ); if( pgno==0 ){ - sqlite3_result_int64(pCtx, recoverPageCount(p)); + i64 nPg = recoverPageCount(p); + sqlite3_result_int64(pCtx, nPg); return; }else{ if( p->pGetPage==0 ){ pStmt = recoverPreparePrintf( p, p->dbIn, "SELECT data FROM sqlite_dbpage(%Q) WHERE pgno=?", p->zDb - ); + ); }else{ pStmt = p->pGetPage; } @@ -321,6 +360,9 @@ static int recoverOpenOutput(sqlite3_recover *p){ assert( p->dbOut==0 ); rc = sqlite3_open_v2(p->zUri, &db, flags, 0); + if( rc==SQLITE_OK ){ + rc = sqlite3_exec(db, "PRAGMA writable_schema = 1", 0, 0, 0); + } if( rc==SQLITE_OK ){ const char *zPath = p->zStateDb ? p->zStateDb : ":memory:"; char *zSql = sqlite3_mprintf("ATTACH %Q AS recovery", zPath); @@ -332,6 +374,7 @@ static int recoverOpenOutput(sqlite3_recover *p){ sqlite3_free(zSql); } + if( rc==SQLITE_OK ){ sqlite3_backup *pBackup = sqlite3_backup_init(db, "main", db, "recovery"); if( pBackup ){ @@ -349,6 +392,11 @@ static int recoverOpenOutput(sqlite3_recover *p){ db, "getpage", 1, SQLITE_UTF8, (void*)p, recoverGetPage, 0, 0 ); } + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function( + db, "page_is_used", 1, SQLITE_UTF8, (void*)p, recoverPageIsUsed, 0, 0 + ); + } if( rc!=SQLITE_OK ){ if( p->errCode==SQLITE_OK ) rc = recoverDbError(p, db); @@ -392,6 +440,7 @@ static void recoverAddTable(sqlite3_recover *p, const char *zName, i64 iRoot){ ); if( pStmt ){ + int iPk = -1; RecoverTable *pNew = 0; int nCol = 0; int nName = recoverStrlen(zName); @@ -406,24 +455,37 @@ static void recoverAddTable(sqlite3_recover *p, const char *zName, i64 iRoot){ pNew = recoverMalloc(p, nByte); if( pNew ){ int i = 0; + int iField = 0; + int iBind = 1; char *csr = 0; pNew->aCol = (RecoverColumn*)&pNew[1]; pNew->zTab = csr = (char*)&pNew->aCol[nCol]; pNew->nCol = nCol; pNew->iRoot = iRoot; - pNew->iPk = -1; memcpy(csr, zName, nName); csr += nName+1; for(i=0; sqlite3_step(pStmt)==SQLITE_ROW; i++){ - int bPk = sqlite3_column_int(pStmt, 5); + int iPKF = sqlite3_column_int(pStmt, 5); int n = sqlite3_column_bytes(pStmt, 1); const char *z = (const char*)sqlite3_column_text(pStmt, 1); + const char *zType = (const char*)sqlite3_column_text(pStmt, 2); int eHidden = sqlite3_column_int(pStmt, 6); - if( bPk ) pNew->iPk = i; + if( iPk==-1 && iPKF==1 && !sqlite3_stricmp("integer", zType) ) iPk = i; + if( iPKF>1 ) iPk = -2; pNew->aCol[i].zCol = csr; pNew->aCol[i].eHidden = eHidden; + if( eHidden==RECOVER_EHIDDEN_VIRTUAL ){ + pNew->aCol[i].iField = -1; + }else{ + pNew->aCol[i].iField = iField++; + } + if( eHidden!=RECOVER_EHIDDEN_VIRTUAL + && eHidden!=RECOVER_EHIDDEN_STORED + ){ + pNew->aCol[i].iBind = iBind++; + } memcpy(csr, z, n); csr += (n+1); } @@ -434,18 +496,26 @@ static void recoverAddTable(sqlite3_recover *p, const char *zName, i64 iRoot){ recoverFinalize(p, pStmt); - pStmt = recoverPreparePrintf(p, p->dbOut, "PRAGMA index_info(%Q)", zName); - if( pStmt && sqlite3_step(pStmt)!=SQLITE_ROW ){ - pNew->bIntkey = 1; - }else{ - pNew->iPk = -1; + pNew->bIntkey = 1; + pStmt = recoverPreparePrintf(p, p->dbOut, "PRAGMA index_xinfo(%Q)", zName); + while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ + int iField = sqlite3_column_int(pStmt, 0); + int iCol = sqlite3_column_int(pStmt, 1); + + assert( iFieldnCol && iColnCol ); + pNew->aCol[iCol].iField = iField; + + pNew->bIntkey = 0; + iPk = -2; } recoverFinalize(p, pStmt); + + if( iPk>=0 ) pNew->aCol[iPk].bIPK = 1; } } /* -** +** */ static int recoverWriteSchema1(sqlite3_recover *p){ sqlite3_stmt *pSelect = 0; @@ -453,7 +523,8 @@ static int recoverWriteSchema1(sqlite3_recover *p){ pSelect = recoverPrepare(p, p->dbOut, "SELECT rootpage, sql, type='table' FROM recovery.schema " - " WHERE type='table' OR (type='index' AND sql LIKE '%unique%')" + " WHERE type='table' OR (type='index' AND sql LIKE '%unique%') " + " ORDER BY type!='table', name!='sqlite_sequence'" ); pTblname = recoverPrepare(p, p->dbOut, @@ -545,8 +616,9 @@ static sqlite3_stmt *recoverInsertStmt( if( eHidden!=RECOVER_EHIDDEN_VIRTUAL && eHidden!=RECOVER_EHIDDEN_STORED ){ + assert( pTab->aCol[ii].iField>=0 && pTab->aCol[ii].iBind>=1 ); zSql = recoverMPrintf(p, "%z%s%Q", zSql, zSep, pTab->aCol[ii].zCol); - zBind = recoverMPrintf(p, "%z%s?", zBind, zSep); + zBind = recoverMPrintf(p, "%z%s?%d", zBind, zSep, pTab->aCol[ii].iBind); zSep = ", "; } } @@ -565,74 +637,268 @@ static RecoverTable *recoverFindTable(sqlite3_recover *p, u32 iRoot){ return pRet; } -static int recoverWriteLostAndFound(sqlite3_recover *p){ +/* +** This function attempts to create a lost and found table within the +** output db. If successful, it returns a pointer to a buffer containing +** the name of the new table. It is the responsibility of the caller to +** eventually free this buffer using sqlite3_free(). +** +** If an error occurs, NULL is returned and an error code and error +** message left in the recover handle. +*/ +static char *recoverLostAndFoundCreate( + sqlite3_recover *p, /* Recover object */ + int nField /* Number of column fields in new table */ +){ + char *zTbl = 0; + sqlite3_stmt *pProbe = 0; + int ii = 0; + + pProbe = recoverPrepare(p, p->dbOut, + "SELECT 1 FROM sqlite_schema WHERE name=?" + ); + for(ii=-1; zTbl==0 && p->errCode==SQLITE_OK && ii<1000; ii++){ + int bFail = 0; + if( ii<0 ){ + zTbl = recoverPrintf(p, "%s", p->zLostAndFound); + }else{ + zTbl = recoverPrintf(p, "%s_%d", p->zLostAndFound, ii); + } + + if( p->errCode==SQLITE_OK ){ + sqlite3_bind_text(pProbe, 1, zTbl, -1, SQLITE_STATIC); + if( SQLITE_ROW==sqlite3_step(pProbe) ){ + bFail = 1; + } + recoverReset(p, pProbe); + } + + if( bFail ){ + sqlite3_clear_bindings(pProbe); + sqlite3_free(zTbl); + zTbl = 0; + } + } + recoverFinalize(p, pProbe); + + if( zTbl ){ + const char *zSep = 0; + char *zField = 0; + char *zSql = 0; + + zSep = "rootpgno INTEGER, pgno INTEGER, nfield INTEGER, id INTEGER, "; + for(ii=0; p->errCode==SQLITE_OK && iidbOut, zSql); + sqlite3_free(zSql); + }else if( p->errCode==SQLITE_OK ){ + recoverError( + p, SQLITE_ERROR, "failed to create %s output table", p->zLostAndFound + ); + } + + return zTbl; +} + +/* +** Synthesize and prepare an INSERT statement to write to the lost_and_found +** table in the output database. The name of the table is zTab, and it has +** nField c* fields. +*/ +static sqlite3_stmt *recoverLostAndFoundInsert( + sqlite3_recover *p, + const char *zTab, + int nField +){ + int nTotal = nField + 4; + int ii; + char *zBind = 0; + const char *zSep = ""; + sqlite3_stmt *pRet = 0; + + for(ii=0; iidbOut, "INSERT INTO %s VALUES(%s)", zTab, zBind + ); + sqlite3_free(zBind); + return pRet; +} + +static void recoverLostAndFoundPopulate( + sqlite3_recover *p, + sqlite3_stmt *pInsert, + int nField +){ + sqlite3_stmt *pStmt = recoverPrepare(p, p->dbOut, + "WITH RECURSIVE pages(root, page) AS (" + " SELECT pgno, pgno FROM recovery.map WHERE parent IS NULL" + " UNION" + " SELECT root, child FROM sqlite_dbptr('getpage()'), pages " + " WHERE pgno=page" + ") " + "SELECT root, page, cell, field, value " + "FROM sqlite_dbdata('getpage()') d, pages p WHERE p.page=d.pgno " + " AND NOT page_is_used(page) " + "UNION ALL " + "SELECT 0, 0, 0, 0, 0" + ); + + sqlite3_value **apVal = 0; + int nVal = -1; + i64 iRowid = 0; + int bHaveRowid = 0; + int ii; + + i64 iPrevRoot = -1; + i64 iPrevPage = -1; + int iPrevCell = -1; + + apVal = (sqlite3_value**)recoverMalloc(p, nField*sizeof(sqlite3_value*)); + while( p->errCode==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ + i64 iRoot = sqlite3_column_int64(pStmt, 0); + i64 iPage = sqlite3_column_int64(pStmt, 1); + int iCell = sqlite3_column_int64(pStmt, 2); + int iField = sqlite3_column_int64(pStmt, 3); + + if( iPrevRoot>0 && ( + iPrevRoot!=iRoot || iPrevPage!=iPage || iPrevCell!=iCell + )){ + /* Insert the new row */ + sqlite3_bind_int64(pInsert, 1, iPrevRoot); /* rootpgno */ + sqlite3_bind_int64(pInsert, 2, iPrevPage); /* pgno */ + sqlite3_bind_int(pInsert, 3, nVal); /* nfield */ + if( bHaveRowid ){ + sqlite3_bind_int64(pInsert, 4, iRowid); /* id */ + } + for(ii=0; iipUsed = recoverBitmapAlloc(p, nPg); if( pMap ){ - sqlite3_stmt *pStmt = 0; - char *zField = 0; - const char *zSep = 0; - int ii; + char *zTab = 0; /* Name of lost_and_found table */ + sqlite3_stmt *pInsert = 0; /* INSERT INTO lost_and_found ... */ + int nField = 0; + /* Add all pages that are part of any tree in the recoverable part of + ** the input database schema to the bitmap. */ sqlite3_stmt *pStmt = recoverPrepare( p, p->dbOut, - "WITH RECURSIVE used(page) AS (" + "WITH roots(r) AS (" + " SELECT 1 UNION ALL" " SELECT rootpage FROM recovery.schema WHERE rootpage>0" + ")," + "used(page) AS (" + " SELECT r FROM roots" " UNION" " SELECT child FROM sqlite_dbptr('getpage()'), used " " WHERE pgno=page" ") " "SELECT page FROM used" ); - while( pStmt && sqlite3_step(pStmt) ){ - i64 iPg = sqlite3_column_int64(pStmt); + while( pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){ + i64 iPg = sqlite3_column_int64(pStmt, 0); recoverBitmapSet(pMap, iPg); } - recoverFinalize(pStmt); + recoverFinalize(p, pStmt); + /* Add an entry for each page not already added to the bitmap to + ** the recovery.map table. This loop leaves the "parent" column + ** of each recovery.map row set to NULL - to be filled in below. */ pStmt = recoverPreparePrintf( p, p->dbOut, "WITH RECURSIVE seq(ii) AS (" " SELECT 1 UNION ALL SELECT ii+1 FROM seq WHERE ii<%lld" ")" - "INSERT INTO recover.map(pgno) " - " SELECT ii FROM seq WHERE !page_is_used(ii)" + "INSERT INTO recovery.map(pgno) " + " SELECT ii FROM seq WHERE NOT page_is_used(ii)", nPg ); sqlite3_step(pStmt); - recoverFinalize(pStmt); + recoverFinalize(p, pStmt); + /* Set the "parent" column for each row of the recovery.map table */ pStmt = recoverPrepare( p, p->dbOut, - "UPDATE recover.map SET parent = ptr.pgno " - " FROM sqlite_dbptr('getpage()') WHERE recover.map.pgno=ptr.child" + "UPDATE recovery.map SET parent = ptr.pgno " + " FROM sqlite_dbptr('getpage()') AS ptr " + " WHERE recovery.map.pgno=ptr.child" ); sqlite3_step(pStmt); - recoverFinalize(pStmt); + recoverFinalize(p, pStmt); + /* Figure out the number of fields in the longest record that will be + ** recovered into the lost_and_found table. Set nField to this value. */ pStmt = recoverPrepare( p, p->dbOut, - "SELECT max(field) FROM sqlite_dbdata('getpage') WHERE pgno IN (" - " SELECT pgno FROM recover.map" + "SELECT max(field)+1 FROM sqlite_dbdata('getpage') WHERE pgno IN (" + " SELECT pgno FROM recovery.map" ")" ); - if( pStmt && sqlite3_step(pStmt) ){ - nMaxField = sqlite3_column_int64(pStmt, 0); + if( pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){ + nField = sqlite3_column_int64(pStmt, 0); } - recoverFinalize(pStmt); + recoverFinalize(p, pStmt); - if( nMaxField==0 || p->errCode!=SQLITE_OK ) return p->errCode; - - zSep = "rootpgno INTEGER, pgno INTEGER, nfield INTEGER, id INTEGER, "; - for(ii=0; iierrCode = SQLITE_NOMEM; - } + if( nField>0 ){ + zTab = recoverLostAndFoundCreate(p, nField); + pInsert = recoverLostAndFoundInsert(p, zTab, nField); + recoverLostAndFoundPopulate(p, pInsert, nField); + recoverFinalize(p, pInsert); + sqlite3_free(zTab); } + + recoverBitmapFree(pMap); + p->pUsed = 0; } } @@ -647,7 +913,7 @@ static int recoverWriteData(sqlite3_recover *p){ if( pTbl->nCol>nMax ) nMax = pTbl->nCol; } - apVal = (sqlite3_value**)recoverMalloc(p, sizeof(sqlite3_value*) * nMax); + apVal = (sqlite3_value**)recoverMalloc(p, sizeof(sqlite3_value*) * (nMax+1)); if( apVal==0 ) return p->errCode; pSel = recoverPrepare(p, p->dbOut, @@ -697,26 +963,15 @@ static int recoverWriteData(sqlite3_recover *p){ nInsert = nVal; } - for(ii=0; iinCol && iValaCol[ii].eHidden; - switch( eHidden ){ - case RECOVER_EHIDDEN_NONE: - case RECOVER_EHIDDEN_HIDDEN: - if( ii==pTab->iPk ){ - sqlite3_bind_int64(pInsert, iBind, iRowid); - }else{ - sqlite3_bind_value(pInsert, iBind, apVal[iVal]); - } - iBind++; - iVal++; - break; + for(ii=0; iinCol; ii++){ + RecoverColumn *pCol = &pTab->aCol[ii]; - case RECOVER_EHIDDEN_VIRTUAL: - break; - - case RECOVER_EHIDDEN_STORED: - iVal++; - break; + if( pCol->iBind>0 ){ + if( pCol->bIPK ){ + sqlite3_bind_int64(pInsert, pCol->iBind, iRowid); + }else if( pCol->iFieldiBind, apVal[pCol->iField]); + } } } @@ -811,7 +1066,13 @@ int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){ break; case SQLITE_RECOVER_LOST_AND_FOUND: - p->bLostAndFound = (pArg ? 1 : 0); + const char *zArg = (const char*)pArg; + sqlite3_free(p->zLostAndFound); + if( zArg ){ + p->zLostAndFound = recoverPrintf(p, "%s", zArg); + }else{ + p->zLostAndFound = 0; + } break; default: @@ -832,7 +1093,7 @@ static void recoverStep(sqlite3_recover *p){ if( recoverCacheSchema(p) ) return; if( recoverWriteSchema1(p) ) return; if( recoverWriteData(p) ) return; - if( p->bLostAndFound && recoverWriteLostAndFound(p) ) return; + if( p->zLostAndFound && recoverLostAndFound(p) ) return; if( recoverWriteSchema2(p) ) return; } } @@ -861,6 +1122,7 @@ int sqlite3_recover_finish(sqlite3_recover *p){ rc = p->errCode; sqlite3_free(p->zStateDb); + sqlite3_free(p->zLostAndFound); sqlite3_free(p); return rc; } diff --git a/ext/recover/sqlite3recover.h b/ext/recover/sqlite3recover.h index 0c83f8dea5..8306c67a12 100644 --- a/ext/recover/sqlite3recover.h +++ b/ext/recover/sqlite3recover.h @@ -38,6 +38,18 @@ sqlite3_recover *sqlite3_recover_init( /* Details TBD. */ int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg); +/* +** SQLITE_RECOVER_TESTDB: +** +** +** SQLITE_RECOVER_LOST_AND_FOUND: +** The pArg argument points to a string buffer containing the name +** of a "lost-and-found" table in the output database, or NULL. If +** the argument is non-NULL and the database contains seemingly +** valid pages that cannot be associated with any table in the +** recovered part of the schema, data is extracted from these +** pages to add to the lost-and-found table. +*/ #define SQLITE_RECOVER_TESTDB 789 #define SQLITE_RECOVER_LOST_AND_FOUND 790 diff --git a/ext/recover/test_recover.c b/ext/recover/test_recover.c index 912b8dec5c..cdd5d090fa 100644 --- a/ext/recover/test_recover.c +++ b/ext/recover/test_recover.c @@ -79,7 +79,8 @@ static int testRecoverCmd( switch( iSub ){ case 0: assert( sqlite3_stricmp("config", aSub[iSub].zSub)==0 ); { const char *aOp[] = { - "testdb", /* 0 */ + "testdb", /* 0 */ + "lostandfound", /* 1 */ 0 }; int iOp = 0; @@ -89,8 +90,13 @@ static int testRecoverCmd( } switch( iOp ){ case 0: - res = sqlite3_recover_config( - pTest->p, SQLITE_RECOVER_TESTDB, (void*)Tcl_GetString(objv[3]) + res = sqlite3_recover_config(pTest->p, + SQLITE_RECOVER_TESTDB, (void*)Tcl_GetString(objv[3]) + ); + break; + case 1: + res = sqlite3_recover_config(pTest->p, + SQLITE_RECOVER_LOST_AND_FOUND, (void*)Tcl_GetString(objv[3]) ); break; } diff --git a/manifest b/manifest index 7f8f333f25..10281dc64e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Further\swork\son\smaking\srecovery\sextension\scompatible\swith\sthe\sshell\stool\s".recover"\scode. -D 2022-09-01T21:00:39.747 +C Further\swork\son\smaking\sthe\srecover\sextension\scompatible\swith\sthe\s.recover\scommand. +D 2022-09-03T20:07:39.011 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -299,7 +299,7 @@ F ext/misc/closure.c dbfd8543b2a017ae6b1a5843986b22ddf99ff126ec9634a2f4047cd14c8 F ext/misc/completion.c 6dafd7f4348eecc7be9e920d4b419d1fb2af75d938cd9c59a20cfe8beb2f22b9 F ext/misc/compress.c 3354c77a7c8e86e07d849916000cdac451ed96500bfb5bd83b20eb61eee012c9 F ext/misc/csv.c ca8d6dafc5469639de81937cb66ae2e6b358542aba94c4f791910d355a8e7f73 -F ext/misc/dbdata.c f317980cea788e67932828b94a16ee8a8b859e3c2d62859d09ba3d5ca85f87cb +F ext/misc/dbdata.c 9bb3666519bd8a54cce4934076a557fe6441c5bafce7e9c24d8b5ced148e8154 F ext/misc/dbdump.c b8592f6f2da292c62991a13864a60d6c573c47a9cc58362131b9e6a64f823e01 F ext/misc/decimal.c 09f967dcf4a1ee35a76309829308ec278d3648168733f4a1147820e11ebefd12 F ext/misc/eval.c 04bc9aada78c888394204b4ed996ab834b99726fb59603b0ee3ed6e049755dc1 @@ -389,9 +389,10 @@ F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9c F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a F ext/recover/recover1.test a848af8c82fe0731af835ff99475724f8654d2f24f772cc4e6f7ec4eb2ab71ea F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c -F ext/recover/sqlite3recover.c d81b430f968d838035ebf5ca168b43ae8a0bec1e7c2c950b74ec4fd5e16ca47b -F ext/recover/sqlite3recover.h 94e277a9b314a03df46b5e94cc44b70ed6c6893d2776d09c7ea0b55c969ad854 -F ext/recover/test_recover.c 919f61df54776598b350250057fd2d3ea9cc2cef1aeac0dbb760958d26fe1afb +F ext/recover/recoverold.test 33ccbe2393af0e82f292c135b725e3eca1e803960681cf6da41fc00d28bd8683 +F ext/recover/sqlite3recover.c 8d93b9aa056c3fae9a5e2736a4ffa71414bdb502863ef879e55bec7b37030266 +F ext/recover/sqlite3recover.h b82974790b528480163d87dcd84afffe7568393194c9ec8241cfbc3ee6bbdd1b +F ext/recover/test_recover.c b8dddd96ccd4a62bc14cb3a8d5696407892e184fe7d45ecbedde954577857de2 F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 F ext/repair/checkfreelist.c e21f06995ff4efdc1622dcceaea4dcba2caa83ca2f31a1607b98a8509168a996 F ext/repair/checkindex.c 4383e4469c21e5b9ae321d0d63cec53e981af9d7a6564be6374f0eeb93dfc890 @@ -1341,7 +1342,7 @@ F test/parser1.test 6ccdf5e459a5dc4673d3273dc311a7e9742ca952dd0551a6a6320d27035c F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442 F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff -F test/permutations.test 909c84575ac50f6d30fa125a109a01986e08c26b9ea38d28501a0711b50b5627 +F test/permutations.test 847df2d81f0172ab7032e55145f0f3da460dd65759ac2b02864e385add1947d5 F test/pg_common.tcl 3b27542224db1e713ae387459b5d117c836a5f6e328846922993b6d2b7640d9f F test/pragma.test cae534c12a033a5c319ccc94f50b32811acdef9f67bf19a82ff42697caccd69f F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f @@ -2004,8 +2005,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f8298eeba01cb5b02ac4d642c06f3801331ca90edea533ea898a3283981a9e49 -R 185cdcc7bc591773e93e55f9de4bfb4c +P 8df7c7d0dcd1b2fcdad00e765a9868407f0ced02ac4432ee2cdf25d83b130759 +R 93788fe8081a2011cde8fac3142d518f U dan -Z 5d96e275b1b3f6a2d23b93e2a033e9c9 +Z 21514150d144c868f0bbea27057fe308 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5c5b60080f..093f196c7a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8df7c7d0dcd1b2fcdad00e765a9868407f0ced02ac4432ee2cdf25d83b130759 \ No newline at end of file +f2ac315844d8db1bd1c6950a4fef7c459ddd37cc21a8f3daafa5639fad8118e2 \ No newline at end of file diff --git a/test/permutations.test b/test/permutations.test index c5044e1a6d..8ab9dc8755 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -91,6 +91,7 @@ foreach f [glob -nocomplain \ $testdir/../ext/fts5/test/*.test \ $testdir/../ext/expert/*.test \ $testdir/../ext/lsm1/test/*.test \ + $testdir/../ext/recover/*.test \ ] { lappend alltests $f } From be2e212cf93f6bae68dc75ed0aa9f2b07988cd3f Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 3 Sep 2022 20:31:36 +0000 Subject: [PATCH 046/428] Take the freelist into account when recovering data that is not linked in to any tree associated with a schema entry. FossilOrigin-Name: dbd1f1efb349a9c8886e42b3f07d3f4c576924136f111558c7294d0a272e415a --- ext/recover/recoverold.test | 17 ++++++++++ ext/recover/sqlite3recover.c | 64 ++++++++++++++++++++++++++++++++++-- manifest | 14 ++++---- manifest.uuid | 2 +- 4 files changed, 86 insertions(+), 11 deletions(-) diff --git a/ext/recover/recoverold.test b/ext/recover/recoverold.test index 61f09397b8..001426b88d 100644 --- a/ext/recover/recoverold.test +++ b/ext/recover/recoverold.test @@ -131,6 +131,23 @@ do_recover_test 2.4.1 { 2 2 3 {} 8 9 7 } +do_execsql_test 2.5 { + CREATE TABLE x1(a, b, c); + WITH s(i) AS ( + SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100 + ) + INSERT INTO x1 SELECT i, i, hex(randomblob(500)) FROM s; + DROP TABLE x1; +} +do_recover_test 2.5.1 { + SELECT name FROM sqlite_master; + SELECT * FROM lost_and_found_1; +} {lost_and_found lost_and_found_0 lost_and_found_1 + 2 2 3 {} 2 3 1 + 2 2 3 {} 5 6 4 + 2 2 3 {} 8 9 7 +} + #------------------------------------------------------------------------- breakpoint reset_db diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index ec333f0d05..34cc0cdd50 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -107,7 +107,7 @@ static int recoverStrlen(const char *zStr){ return nRet; } -static void *recoverMalloc(sqlite3_recover *p, sqlite3_int64 nByte){ +static void *recoverMalloc(sqlite3_recover *p, i64 nByte){ void *pRet = 0; assert( nByte>0 ); if( p->errCode==SQLITE_OK ){ @@ -276,6 +276,36 @@ static i64 recoverPageCount(sqlite3_recover *p){ return nPg; } +/* +** Scalar function "read_i32". The first argument to this function +** must be a blob. The second a non-negative integer. This function +** reads and returns a 32-bit big-endian integer from byte +** offset (4*) of the blob. +*/ +static void recoverReadI32( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + const unsigned char *pBlob; + int nBlob; + int iInt; + + assert( argc==2 ); + nBlob = sqlite3_value_bytes(argv[0]); + pBlob = (const unsigned char*)sqlite3_value_blob(argv[0]); + iInt = sqlite3_value_int(argv[1]); + + if( iInt>=0 && (iInt+1)*4<=nBlob ){ + const unsigned char *a = &pBlob[iInt*4]; + i64 iVal = ((i64)a[0]<<24) + + ((i64)a[1]<<16) + + ((i64)a[2]<< 8) + + ((i64)a[3]<< 0); + sqlite3_result_int64(context, iVal); + } +} + /* ** SELECT page_is_used(pgno); */ @@ -285,7 +315,7 @@ static void recoverPageIsUsed( sqlite3_value **apArg ){ sqlite3_recover *p = (sqlite3_recover*)sqlite3_user_data(pCtx); - sqlite3_int64 pgno = sqlite3_value_int64(apArg[0]); + i64 pgno = sqlite3_value_int64(apArg[0]); sqlite3_stmt *pStmt = 0; int bRet; @@ -314,7 +344,7 @@ static void recoverGetPage( sqlite3_value **apArg ){ sqlite3_recover *p = (sqlite3_recover*)sqlite3_user_data(pCtx); - sqlite3_int64 pgno = sqlite3_value_int64(apArg[0]); + i64 pgno = sqlite3_value_int64(apArg[0]); sqlite3_stmt *pStmt = 0; assert( nArg==1 ); @@ -397,6 +427,11 @@ static int recoverOpenOutput(sqlite3_recover *p){ db, "page_is_used", 1, SQLITE_UTF8, (void*)p, recoverPageIsUsed, 0, 0 ); } + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function( + db, "read_i32", 2, SQLITE_UTF8, (void*)p, recoverReadI32, 0, 0 + ); + } if( rc!=SQLITE_OK ){ if( p->errCode==SQLITE_OK ) rc = recoverDbError(p, db); @@ -852,6 +887,29 @@ static int recoverLostAndFound(sqlite3_recover *p){ } recoverFinalize(p, pStmt); + /* Add all pages that appear to be part of the freelist to the bitmap. */ + pStmt = recoverPrepare(p, p->dbOut, + "WITH trunk(pgno) AS (" + " SELECT read_i32(getpage(1), 8) AS x WHERE x>0" + " UNION" + " SELECT read_i32(getpage(trunk.pgno), 0) AS x FROM trunk WHERE x>0" + ")," + "trunkdata(pgno, data) AS (" + " SELECT pgno, getpage(pgno) FROM trunk" + ")," + "freelist(data, n, freepgno) AS (" + " SELECT data, min(16384, read_i32(data, 1)-1), pgno FROM trunkdata" + " UNION ALL" + " SELECT data, n-1, read_i32(data, 2+n) FROM freelist WHERE n>=0" + ")" + "SELECT freepgno FROM freelist" + ); + while( pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){ + i64 iPg = sqlite3_column_int64(pStmt, 0); + recoverBitmapSet(pMap, iPg); + } + recoverFinalize(p, pStmt); + /* Add an entry for each page not already added to the bitmap to ** the recovery.map table. This loop leaves the "parent" column ** of each recovery.map row set to NULL - to be filled in below. */ diff --git a/manifest b/manifest index 10281dc64e..00fb244d2e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Further\swork\son\smaking\sthe\srecover\sextension\scompatible\swith\sthe\s.recover\scommand. -D 2022-09-03T20:07:39.011 +C Take\sthe\sfreelist\sinto\saccount\swhen\srecovering\sdata\sthat\sis\snot\slinked\sin\sto\sany\stree\sassociated\swith\sa\sschema\sentry. +D 2022-09-03T20:31:36.832 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -389,8 +389,8 @@ F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9c F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a F ext/recover/recover1.test a848af8c82fe0731af835ff99475724f8654d2f24f772cc4e6f7ec4eb2ab71ea F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c -F ext/recover/recoverold.test 33ccbe2393af0e82f292c135b725e3eca1e803960681cf6da41fc00d28bd8683 -F ext/recover/sqlite3recover.c 8d93b9aa056c3fae9a5e2736a4ffa71414bdb502863ef879e55bec7b37030266 +F ext/recover/recoverold.test 7578e9b938db15dc469a4af247e15866226f366bde0cbe09a40b0aef4a0506c8 +F ext/recover/sqlite3recover.c 6c9cbc993a970060f9fb881d78f6c7e182ec988a5e48acbf15bb4a5f05ce2902 F ext/recover/sqlite3recover.h b82974790b528480163d87dcd84afffe7568393194c9ec8241cfbc3ee6bbdd1b F ext/recover/test_recover.c b8dddd96ccd4a62bc14cb3a8d5696407892e184fe7d45ecbedde954577857de2 F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 @@ -2005,8 +2005,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 8df7c7d0dcd1b2fcdad00e765a9868407f0ced02ac4432ee2cdf25d83b130759 -R 93788fe8081a2011cde8fac3142d518f +P f2ac315844d8db1bd1c6950a4fef7c459ddd37cc21a8f3daafa5639fad8118e2 +R 471b646b541e0fcab850e84cb036ac46 U dan -Z 21514150d144c868f0bbea27057fe308 +Z 9f579b130a06a2078244b88aebbd5365 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 093f196c7a..f01f3f35e4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f2ac315844d8db1bd1c6950a4fef7c459ddd37cc21a8f3daafa5639fad8118e2 \ No newline at end of file +dbd1f1efb349a9c8886e42b3f07d3f4c576924136f111558c7294d0a272e415a \ No newline at end of file From 7302079dbe01c7b136ccc0853816bd7cc4319dde Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 3 Sep 2022 21:08:38 +0000 Subject: [PATCH 047/428] Add an option to assume the freelist is corrupt when recovering data. FossilOrigin-Name: 253e498f5200b8b3e2bc309587af108dd1cec8a884b3d2a49d5406525c9a4b4c --- ext/recover/recoverold.test | 13 +++++++++- ext/recover/sqlite3recover.c | 48 ++++++++++++++++++++---------------- ext/recover/sqlite3recover.h | 16 +++++++++--- ext/recover/test_recover.c | 9 +++++++ manifest | 18 +++++++------- manifest.uuid | 2 +- 6 files changed, 71 insertions(+), 35 deletions(-) diff --git a/ext/recover/recoverold.test b/ext/recover/recoverold.test index 001426b88d..8f70a7473d 100644 --- a/ext/recover/recoverold.test +++ b/ext/recover/recoverold.test @@ -47,7 +47,6 @@ proc do_recover_test {tn {tsql {}} {res {}}} { set R [sqlite3_recover_init db main test.db2] $R config lostandfound lost_and_found - $R config testdb rstate.db $R step $R finish @@ -148,6 +147,18 @@ do_recover_test 2.5.1 { 2 2 3 {} 8 9 7 } +do_test 2.6 { + forcedelete test.db2 + set R [sqlite3_recover_init db main test.db2] + $R config lostandfound lost_and_found + $R config freelistcorrupt 1 + $R step + $R finish + sqlite3 db2 test.db2 + execsql { SELECT count(*) FROM lost_and_found_1; } db2 +} {103} +db2 close + #------------------------------------------------------------------------- breakpoint reset_db diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index 34cc0cdd50..14cc29e476 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -93,7 +93,7 @@ struct sqlite3_recover { char *zStateDb; char *zLostAndFound; /* Name of lost-and-found table (or NULL) */ - + int bFreelistCorrupt; }; /* @@ -888,27 +888,29 @@ static int recoverLostAndFound(sqlite3_recover *p){ recoverFinalize(p, pStmt); /* Add all pages that appear to be part of the freelist to the bitmap. */ - pStmt = recoverPrepare(p, p->dbOut, - "WITH trunk(pgno) AS (" - " SELECT read_i32(getpage(1), 8) AS x WHERE x>0" - " UNION" - " SELECT read_i32(getpage(trunk.pgno), 0) AS x FROM trunk WHERE x>0" - ")," - "trunkdata(pgno, data) AS (" - " SELECT pgno, getpage(pgno) FROM trunk" - ")," - "freelist(data, n, freepgno) AS (" - " SELECT data, min(16384, read_i32(data, 1)-1), pgno FROM trunkdata" - " UNION ALL" - " SELECT data, n-1, read_i32(data, 2+n) FROM freelist WHERE n>=0" - ")" - "SELECT freepgno FROM freelist" - ); - while( pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){ - i64 iPg = sqlite3_column_int64(pStmt, 0); - recoverBitmapSet(pMap, iPg); + if( p->bFreelistCorrupt==0 ){ + pStmt = recoverPrepare(p, p->dbOut, + "WITH trunk(pgno) AS (" + " SELECT read_i32(getpage(1), 8) AS x WHERE x>0" + " UNION" + " SELECT read_i32(getpage(trunk.pgno), 0) AS x FROM trunk WHERE x>0" + ")," + "trunkdata(pgno, data) AS (" + " SELECT pgno, getpage(pgno) FROM trunk" + ")," + "freelist(data, n, freepgno) AS (" + " SELECT data, min(16384, read_i32(data, 1)-1), pgno FROM trunkdata" + " UNION ALL" + " SELECT data, n-1, read_i32(data, 2+n) FROM freelist WHERE n>=0" + ")" + "SELECT freepgno FROM freelist" + ); + while( pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){ + i64 iPg = sqlite3_column_int64(pStmt, 0); + recoverBitmapSet(pMap, iPg); + } + recoverFinalize(p, pStmt); } - recoverFinalize(p, pStmt); /* Add an entry for each page not already added to the bitmap to ** the recovery.map table. This loop leaves the "parent" column @@ -1133,6 +1135,10 @@ int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){ } break; + case SQLITE_RECOVER_FREELIST_CORRUPT: + p->bFreelistCorrupt = (pArg ? 1 : 0); + break; + default: rc = SQLITE_NOTFOUND; break; diff --git a/ext/recover/sqlite3recover.h b/ext/recover/sqlite3recover.h index 8306c67a12..638a8b1c92 100644 --- a/ext/recover/sqlite3recover.h +++ b/ext/recover/sqlite3recover.h @@ -41,7 +41,6 @@ int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg); /* ** SQLITE_RECOVER_TESTDB: ** -** ** SQLITE_RECOVER_LOST_AND_FOUND: ** The pArg argument points to a string buffer containing the name ** of a "lost-and-found" table in the output database, or NULL. If @@ -49,9 +48,20 @@ int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg); ** valid pages that cannot be associated with any table in the ** recovered part of the schema, data is extracted from these ** pages to add to the lost-and-found table. +** +** SQLITE_RECOVER_FREELIST_CORRUPT: +** The pArg value must actually be integer (type "int") value 0 or 1 +** cast as a (void*). If this option is set (argument is 1) and +** a lost-and-found table has been configured using +** SQLITE_RECOVER_LOST_AND_FOUND, then is assumed that the freelist is +** corrupt and an attempt is made to recover records from pages that +** appear to be linked into the freelist. Otherwise, pages on the freelist +** are ignored. Setting this option can recover more data from the +** database, but often ends up "recovering" deleted records. */ -#define SQLITE_RECOVER_TESTDB 789 -#define SQLITE_RECOVER_LOST_AND_FOUND 790 +#define SQLITE_RECOVER_TESTDB 789 +#define SQLITE_RECOVER_LOST_AND_FOUND 790 +#define SQLITE_RECOVER_FREELIST_CORRUPT 791 /* Step the recovery object. Return SQLITE_DONE if recovery is complete, ** SQLITE_OK if recovery is not complete but no error has occurred, or diff --git a/ext/recover/test_recover.c b/ext/recover/test_recover.c index cdd5d090fa..835a203f66 100644 --- a/ext/recover/test_recover.c +++ b/ext/recover/test_recover.c @@ -81,6 +81,7 @@ static int testRecoverCmd( const char *aOp[] = { "testdb", /* 0 */ "lostandfound", /* 1 */ + "freelistcorrupt", /* 2 */ 0 }; int iOp = 0; @@ -99,6 +100,14 @@ static int testRecoverCmd( SQLITE_RECOVER_LOST_AND_FOUND, (void*)Tcl_GetString(objv[3]) ); break; + case 2: { + int iVal = 0; + if( Tcl_GetIntFromObj(interp, objv[3], &iVal) ) return TCL_ERROR; + res = sqlite3_recover_config(pTest->p, + SQLITE_RECOVER_FREELIST_CORRUPT, (void*)iVal + ); + break; + } } Tcl_SetObjResult(interp, Tcl_NewIntObj(res)); break; diff --git a/manifest b/manifest index 00fb244d2e..04d150cbd7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Take\sthe\sfreelist\sinto\saccount\swhen\srecovering\sdata\sthat\sis\snot\slinked\sin\sto\sany\stree\sassociated\swith\sa\sschema\sentry. -D 2022-09-03T20:31:36.832 +C Add\san\soption\sto\sassume\sthe\sfreelist\sis\scorrupt\swhen\srecovering\sdata. +D 2022-09-03T21:08:38.958 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -389,10 +389,10 @@ F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9c F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a F ext/recover/recover1.test a848af8c82fe0731af835ff99475724f8654d2f24f772cc4e6f7ec4eb2ab71ea F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c -F ext/recover/recoverold.test 7578e9b938db15dc469a4af247e15866226f366bde0cbe09a40b0aef4a0506c8 -F ext/recover/sqlite3recover.c 6c9cbc993a970060f9fb881d78f6c7e182ec988a5e48acbf15bb4a5f05ce2902 -F ext/recover/sqlite3recover.h b82974790b528480163d87dcd84afffe7568393194c9ec8241cfbc3ee6bbdd1b -F ext/recover/test_recover.c b8dddd96ccd4a62bc14cb3a8d5696407892e184fe7d45ecbedde954577857de2 +F ext/recover/recoverold.test e7e00c78ec35b60488369ddf99e36a3b30e686566571969b05781e5063bdffe8 +F ext/recover/sqlite3recover.c 395c9f623cf84bd8c2e651ec112898d81e1908bbda66fe5f0efcfaa85ad2b262 +F ext/recover/sqlite3recover.h 35aacde3b3834d8ceefb20a2cf0ba221cbb5d802efc11a0529aafc018c462e13 +F ext/recover/test_recover.c 112c580e7cd765a20bbc94998f8b43b629db47fa6bfd696484ca722e418f4172 F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 F ext/repair/checkfreelist.c e21f06995ff4efdc1622dcceaea4dcba2caa83ca2f31a1607b98a8509168a996 F ext/repair/checkindex.c 4383e4469c21e5b9ae321d0d63cec53e981af9d7a6564be6374f0eeb93dfc890 @@ -2005,8 +2005,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f2ac315844d8db1bd1c6950a4fef7c459ddd37cc21a8f3daafa5639fad8118e2 -R 471b646b541e0fcab850e84cb036ac46 +P dbd1f1efb349a9c8886e42b3f07d3f4c576924136f111558c7294d0a272e415a +R f1692b1a741d9ca9573411dcac80d98d U dan -Z 9f579b130a06a2078244b88aebbd5365 +Z 9a22d599f360a7b8179802a74accdf31 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f01f3f35e4..9852bbbed1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -dbd1f1efb349a9c8886e42b3f07d3f4c576924136f111558c7294d0a272e415a \ No newline at end of file +253e498f5200b8b3e2bc309587af108dd1cec8a884b3d2a49d5406525c9a4b4c \ No newline at end of file From 49cb8d7314f201bcc264ab3c38c01ae48822a7a4 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 5 Sep 2022 13:24:08 +0000 Subject: [PATCH 048/428] Minor cleanups in OO API #1. Add Sudoku SQL to batch-runner.js's list. FossilOrigin-Name: 4488cb5798f34cbba14eb8e2aa5da8420c14a85d37d278d357406aedd5c3a9e5 --- ext/wasm/api/sqlite3-api-oo1.js | 86 ++++++++++++++++++--------------- ext/wasm/batch-runner.html | 4 +- ext/wasm/sql/001-sudoku.sql | 28 +++++++++++ manifest | 15 +++--- manifest.uuid | 2 +- 5 files changed, 86 insertions(+), 49 deletions(-) create mode 100644 ext/wasm/sql/001-sudoku.sql diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 2cfb478865..3d24a7689a 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -126,7 +126,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ try { const ppDb = capi.wasm.scopedAllocPtr() /* output (sqlite3**) arg */; const rc = capi.sqlite3_open_v2(fn, ppDb, oflags, null); - ptr = capi.wasm.getMemValue(ppDb, '*'); + ptr = capi.wasm.getPtrValue(ppDb); ctor.checkRc(ptr, rc); }catch( e ){ if( ptr ) capi.sqlite3_close_v2(ptr); @@ -166,6 +166,15 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ For purposes of passing a Stmt instance to C-style sqlite3 functions, its read-only `pointer` property holds its `sqlite3_stmt*` pointer value. + + Other non-function properties include: + + - `db`: the DB object which created the statement. + + - `columnCount`: the number of result columns in the query, or 0 for + queries which cannot return results. + + - `parameterCount`: the number of bindable paramters in the query. */ const Stmt = function(){ if(BindTypes!==arguments[2]){ @@ -272,22 +281,25 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }; /** - Expects to be given a DB instance or an `sqlite3*` pointer, and an - sqlite3 API result code. If the result code is not falsy, this - function throws an SQLite3Error with an error message from - sqlite3_errmsg(), using dbPtr as the 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. + Expects to be given a DB instance or an `sqlite3*` pointer (may + be null) and an sqlite3 API result code. If the result code is + not falsy, this function throws an SQLite3Error with an error + message from sqlite3_errmsg(), using dbPtr as the db handle, or + sqlite3_errstr() if dbPtr is falsy. 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. */ DB.checkRc = function(dbPtr, sqliteResultCode){ if(sqliteResultCode){ if(dbPtr instanceof DB) dbPtr = dbPtr.pointer; - throw new SQLite3Error([ + throw new SQLite3Error( "sqlite result code",sqliteResultCode+":", - capi.sqlite3_errmsg(dbPtr) || "Unknown db error." - ].join(' ')); + (dbPtr + ? capi.sqlite3_errmsg(dbPtr) + : capi.sqlite3_errstr(sqliteResultCode)) + ); } }; @@ -379,9 +391,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ required to check `stmt.pointer` after calling `prepare()` in order to determine whether the Stmt instance is empty or not. Long-time practice (with other sqlite3 script bindings) - suggests that the empty-prepare case is sufficiently rare (and - useless) that supporting it here would simply hurt overall - usability. + suggests that the empty-prepare case is sufficiently rare that + supporting it here would simply hurt overall usability. */ prepare: function(sql){ affirmDbOpen(this); @@ -390,7 +401,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ try{ ppStmt = capi.wasm.scopedAllocPtr()/* output (sqlite3_stmt**) arg */; DB.checkRc(this, capi.sqlite3_prepare_v2(this.pointer, sql, -1, ppStmt, null)); - pStmt = capi.wasm.getMemValue(ppStmt, '*'); + pStmt = capi.wasm.getPtrValue(ppStmt); } finally {capi.wasm.scopedAllocPop(stack)} if(!pStmt) toss3("Cannot prepare empty SQL."); @@ -412,22 +423,21 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ The optional options object may contain any of the following properties: - - .sql = the SQL to run (unless it's provided as the first + - `.sql` = the SQL to run (unless it's provided as the first argument). This must be of type string, Uint8Array, or an array of strings. In the latter case they're concatenated together as-is, _with no separator_ between elements, before evaluation. The array form is often simpler for long hand-written queries. - - .bind = a single value valid as an argument for + - `.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.) - - .saveSql = an optional array. If set, the SQL of each + - `.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. + statement is executed (but after it is prepared - we don't have + the string until after that). Empty SQL statements are elided. ================================================================== The following options apply _only_ to the _first_ statement @@ -435,8 +445,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ the statement actually produces any result rows. ================================================================== - - .callback = a function which gets called for each row of the - result set, but only if that statement has any result + - `.callback` = a function which gets called for each row of + the result set, but only if that statement has any result _rows_. The callback's "this" is the options object. The second argument passed to the callback is always the current Stmt object (so that the caller may collect column names, or @@ -455,24 +465,24 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ The first argument passed to the callback defaults to an array of values from the current result row but may be changed with ... - - .rowMode = specifies the type of he callback's first argument. + - `.rowMode` = specifies the type of he callback's first argument. It may be any of... A) A string describing what type of argument should be passed as the first argument to the callback: - A.1) 'array' (the default) causes the results of + A.1) `'array'` (the default) causes the results of `stmt.get([])` to be passed to passed on and/or appended to `resultRows`. - A.2) 'object' causes the results of + A.2) `'object'` causes the results of `stmt.get(Object.create(null))` to be passed to the `callback` and/or appended to `resultRows`. Achtung: an SQL result may have multiple columns with identical names. In that case, the right-most column will be the one set in this object! - A.3) 'stmt' causes the current Stmt to be passed to the + A.3) `'stmt'` causes the current Stmt to be passed to the callback, but this mode will trigger an exception if `resultRows` is an array because appending the statement to the array would be unhelpful. @@ -491,7 +501,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ Any other `rowMode` value triggers an exception. - - .resultRows: if this is an array, it functions similarly to + - `.resultRows`: if this is an array, it functions similarly to the `callback` option: each row of the result set (if any), with the exception that the `rowMode` 'stmt' is not legal. It is legal to use both `resultRows` and `callback`, but @@ -499,12 +509,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ and can be used over a WebWorker-style message interface. exec() throws if `resultRows` is set and `rowMode` is 'stmt'. - - .columnNames: if this is an array, the column names of the + - `.columnNames`: if this is an array, the column names of the result set are stored in this array before the callback (if any) is triggered (regardless of whether the query produces any result rows). If no statement has result columns, this value is - unchanged. Achtung: an SQL result may have multiple columns with - identical names. + unchanged. Achtung: an SQL result may have multiple columns + with identical names. */ exec: function(/*(sql [,obj]) || (obj)*/){ affirmDbOpen(this); @@ -517,11 +527,9 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const callback = opt.callback; let resultRows = (Array.isArray(opt.resultRows) ? opt.resultRows : undefined); - let rowMode = (((callback||resultRows) && (undefined!==opt.rowMode)) - ? opt.rowMode : undefined); let stmt; let bind = opt.bind; - let runFirstQuery = !!(arg.cbArg || opt.columnNames) /* true to evaluate the first result-returning query */; + let evalFirstResult = !!(arg.cbArg || opt.columnNames) /* true to evaluate the first result-returning query */; const stack = wasm.scopedAllocPush(); try{ const isTA = util.isSQLableTypedArray(arg.sql) @@ -565,10 +573,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ stmt.bind(bind); bind = null; } - if(runFirstQuery && stmt.columnCount){ + if(evalFirstResult && stmt.columnCount){ /* Only forward SELECT results for the FIRST query in the SQL which potentially has them. */ - runFirstQuery = false; + evalFirstResult = false; if(Array.isArray(opt.columnNames)){ stmt.getColumnNames(opt.columnNames); } @@ -580,7 +588,6 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ stmt._isLocked = false; } }else{ - // Do we need to while(stmt.step()){} here? stmt.step(); } stmt.finalize(); @@ -679,8 +686,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ let i, pVal, valType, arg; const tgt = []; for(i = 0; i < argc; ++i){ - pVal = capi.wasm.getMemValue(pArgv + (capi.wasm.ptrSizeof * i), - capi.wasm.ptrIR); + pVal = capi.wasm.getPtrValue(pArgv + (capi.wasm.ptrSizeof * i)); /** Curiously: despite ostensibly requiring 8-byte alignment, the pArgv array is parcelled into chunks of diff --git a/ext/wasm/batch-runner.html b/ext/wasm/batch-runner.html index 1b8d335ddb..10f7e45df7 100644 --- a/ext/wasm/batch-runner.html +++ b/ext/wasm/batch-runner.html @@ -25,7 +25,9 @@

- This page is for running extracts from the output of speedtest --script. + This page is for batch-running extracts from the output + of speedtest1 --script, as well as other standalone SQL + scripts.

ACHTUNG: this file requires a generated input list file. Run "make batch" from this directory to generate it. diff --git a/ext/wasm/sql/001-sudoku.sql b/ext/wasm/sql/001-sudoku.sql new file mode 100644 index 0000000000..53661b1c35 --- /dev/null +++ b/ext/wasm/sql/001-sudoku.sql @@ -0,0 +1,28 @@ +WITH RECURSIVE + input(sud) AS ( + VALUES('53..7....6..195....98....6.8...6...34..8.3..17...2...6.6....28....419..5....8..79') + ), + digits(z, lp) AS ( + VALUES('1', 1) + UNION ALL SELECT + CAST(lp+1 AS TEXT), lp+1 FROM digits WHERE lp<9 + ), + x(s, ind) AS ( + SELECT sud, instr(sud, '.') FROM input + UNION ALL + SELECT + substr(s, 1, ind-1) || z || substr(s, ind+1), + instr( substr(s, 1, ind-1) || z || substr(s, ind+1), '.' ) + FROM x, digits AS z + WHERE ind>0 + AND NOT EXISTS ( + SELECT 1 + FROM digits AS lp + WHERE z.z = substr(s, ((ind-1)/9)*9 + lp, 1) + OR z.z = substr(s, ((ind-1)%9) + (lp-1)*9 + 1, 1) + OR z.z = substr(s, (((ind-1)/3) % 3) * 3 + + ((ind-1)/27) * 27 + lp + + ((lp-1) / 3) * 6, 1) + ) + ) +SELECT s FROM x WHERE ind=0; diff --git a/manifest b/manifest index ddfd3bdce4..b8a3ffbcd5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C wasm:\sminor\stext\sand\sbuild\scleanups. -D 2022-09-03T11:41:44.176 +C Minor\scleanups\sin\sOO\sAPI\s#1.\sAdd\sSudoku\sSQL\sto\sbatch-runner.js's\slist. +D 2022-09-05T13:24:08.577 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -483,13 +483,13 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 1a12e64060c2cb0defd34656a76a9b1d7ed58459c290249bb31567c806fd44de F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3 -F ext/wasm/api/sqlite3-api-oo1.js 183e863eedaba547ffe4981ab95797813b60da46749992bca400d2646e5ccd82 +F ext/wasm/api/sqlite3-api-oo1.js 8818c96272ed70e8a95d09b10f2381df94a79de0d722ef9057005e6594db2f33 F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db98a17685ab7a34a03953c7a F ext/wasm/api/sqlite3-api-worker1.js 73579555563b789785ae83724014eaf31811073aad9be6596c8336ffb51edd71 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 -F ext/wasm/batch-runner.html 439245594711981831f2775f1426fd20d73a9b1bbf53f2cbbfc1194fa069891c +F ext/wasm/batch-runner.html 2d44d99a556c46f586d3319003dd281dd0eb6a13eeadde3eab05ba81eec9ff8a F ext/wasm/batch-runner.js a727cbbffe63fd17fb5a590dc679f0b13bd51880e8f84b461d7df246417689e8 F ext/wasm/common/SqliteTestUtil.js 7a543e238c2ebda922c85076abda017d0480944fdfee576692a0c3a580319ebd F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f @@ -512,6 +512,7 @@ F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f74181 F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0 F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 +F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 @@ -2013,8 +2014,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P b26e2bc05537a1f451db1bdd36a824d71c3ea25a130b6285f31ff9799d65fa7a -R be49009f54e0ae14227cc16ba0e36553 +P 36ceef94e1935f5e85f79e489cd0ed265a77820fb68329c190794df5e076bf84 +R 7441dbef21a285816a3b01822ba89eea U stephan -Z eee0918a1e799dd377ca7c8eb624a8df +Z 1c45c40df6428914f663fd75d3ed35bb # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 99f4bce223..871f2346e9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -36ceef94e1935f5e85f79e489cd0ed265a77820fb68329c190794df5e076bf84 \ No newline at end of file +4488cb5798f34cbba14eb8e2aa5da8420c14a85d37d278d357406aedd5c3a9e5 \ No newline at end of file From a768b67dcc5880f7078de0135838e0d0a0e7334f Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 5 Sep 2022 15:56:09 +0000 Subject: [PATCH 049/428] Add the SQLITE_RECOVER_ROWIDS option. To specify that rowid values that are not also explicit INTEGER PRIMARY KEY values should be preserved. FossilOrigin-Name: 69cc9aba56a196bbd159bd24868aa5ccc60bed0dc612d09ed8a3ae898f156809 --- ext/recover/sqlite3recover.c | 27 +++++++++++++++++++++++++-- ext/recover/sqlite3recover.h | 3 +++ ext/recover/test_recover.c | 11 ++++++++++- manifest | 16 ++++++++-------- manifest.uuid | 2 +- 5 files changed, 47 insertions(+), 12 deletions(-) diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index 14cc29e476..e0eda09d82 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -49,6 +49,7 @@ struct RecoverTable { int nCol; /* Number of columns in table */ RecoverColumn *aCol; /* Array of columns */ int bIntkey; /* True for intkey, false for without rowid */ + int iRowidBind; /* If >0, bind rowid to INSERT here */ RecoverTable *pNext; }; @@ -94,6 +95,7 @@ struct sqlite3_recover { char *zStateDb; char *zLostAndFound; /* Name of lost-and-found table (or NULL) */ int bFreelistCorrupt; + int bRecoverRowid; }; /* @@ -476,6 +478,7 @@ static void recoverAddTable(sqlite3_recover *p, const char *zName, i64 iRoot){ if( pStmt ){ int iPk = -1; + int iBind = 1; RecoverTable *pNew = 0; int nCol = 0; int nName = recoverStrlen(zName); @@ -491,7 +494,6 @@ static void recoverAddTable(sqlite3_recover *p, const char *zName, i64 iRoot){ if( pNew ){ int i = 0; int iField = 0; - int iBind = 1; char *csr = 0; pNew->aCol = (RecoverColumn*)&pNew[1]; pNew->zTab = csr = (char*)&pNew->aCol[nCol]; @@ -545,7 +547,11 @@ static void recoverAddTable(sqlite3_recover *p, const char *zName, i64 iRoot){ } recoverFinalize(p, pStmt); - if( iPk>=0 ) pNew->aCol[iPk].bIPK = 1; + if( iPk>=0 ){ + pNew->aCol[iPk].bIPK = 1; + }else if( pNew->bIntkey ){ + pNew->iRowidBind = iBind++; + } } } @@ -646,6 +652,13 @@ static sqlite3_stmt *recoverInsertStmt( assert( nField<=pTab->nCol ); zSql = recoverMPrintf(p, "INSERT OR IGNORE INTO %Q(", pTab->zTab); + + if( pTab->iRowidBind ){ + assert( pTab->bIntkey ); + zSql = recoverMPrintf(p, "%z_rowid_", zSql); + zBind = recoverMPrintf(p, "%z?%d", zBind, pTab->iRowidBind); + zSep = ", "; + } for(ii=0; iiaCol[ii].eHidden; if( eHidden!=RECOVER_EHIDDEN_VIRTUAL @@ -995,6 +1008,7 @@ static int recoverWriteData(sqlite3_recover *p){ i64 iPrevRoot = -1; i64 iPrevPage = -1; int iPrevCell = -1; + int bHaveRowid = 0; /* True if iRowid is valid */ i64 iRowid = 0; int nVal = -1; @@ -1034,6 +1048,9 @@ static int recoverWriteData(sqlite3_recover *p){ } } } + if( p->bRecoverRowid && pTab->iRowidBind>0 && bHaveRowid ){ + sqlite3_bind_int64(pInsert, pTab->iRowidBind, iRowid); + } sqlite3_step(pInsert); recoverReset(p, pInsert); @@ -1046,6 +1063,7 @@ static int recoverWriteData(sqlite3_recover *p){ apVal[ii] = 0; } nVal = -1; + bHaveRowid = 0; } if( iRoot==0 ) continue; @@ -1061,6 +1079,7 @@ static int recoverWriteData(sqlite3_recover *p){ iRowid = sqlite3_column_int64(pSel, 4); assert( nVal==-1 ); nVal = 0; + bHaveRowid = 1; }else if( iFieldbFreelistCorrupt = (pArg ? 1 : 0); break; + case SQLITE_RECOVER_ROWIDS: + p->bRecoverRowid = (pArg ? 1 : 0); + break; + default: rc = SQLITE_NOTFOUND; break; diff --git a/ext/recover/sqlite3recover.h b/ext/recover/sqlite3recover.h index 638a8b1c92..0294baddaa 100644 --- a/ext/recover/sqlite3recover.h +++ b/ext/recover/sqlite3recover.h @@ -58,10 +58,13 @@ int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg); ** appear to be linked into the freelist. Otherwise, pages on the freelist ** are ignored. Setting this option can recover more data from the ** database, but often ends up "recovering" deleted records. +** +** SQLITE_RECOVER_ROWIDS: */ #define SQLITE_RECOVER_TESTDB 789 #define SQLITE_RECOVER_LOST_AND_FOUND 790 #define SQLITE_RECOVER_FREELIST_CORRUPT 791 +#define SQLITE_RECOVER_ROWIDS 792 /* Step the recovery object. Return SQLITE_DONE if recovery is complete, ** SQLITE_OK if recovery is not complete but no error has occurred, or diff --git a/ext/recover/test_recover.c b/ext/recover/test_recover.c index 835a203f66..0646ff78b8 100644 --- a/ext/recover/test_recover.c +++ b/ext/recover/test_recover.c @@ -82,6 +82,7 @@ static int testRecoverCmd( "testdb", /* 0 */ "lostandfound", /* 1 */ "freelistcorrupt", /* 2 */ + "rowids", /* 3 */ 0 }; int iOp = 0; @@ -102,12 +103,20 @@ static int testRecoverCmd( break; case 2: { int iVal = 0; - if( Tcl_GetIntFromObj(interp, objv[3], &iVal) ) return TCL_ERROR; + if( Tcl_GetBooleanFromObj(interp, objv[3], &iVal) ) return TCL_ERROR; res = sqlite3_recover_config(pTest->p, SQLITE_RECOVER_FREELIST_CORRUPT, (void*)iVal ); break; } + case 3: { + int iVal = 0; + if( Tcl_GetBooleanFromObj(interp, objv[3], &iVal) ) return TCL_ERROR; + res = sqlite3_recover_config(pTest->p, + SQLITE_RECOVER_ROWIDS, (void*)iVal + ); + break; + } } Tcl_SetObjResult(interp, Tcl_NewIntObj(res)); break; diff --git a/manifest b/manifest index 04d150cbd7..9f01e7c919 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\san\soption\sto\sassume\sthe\sfreelist\sis\scorrupt\swhen\srecovering\sdata. -D 2022-09-03T21:08:38.958 +C Add\sthe\sSQLITE_RECOVER_ROWIDS\soption.\sTo\sspecify\sthat\srowid\svalues\sthat\sare\snot\salso\sexplicit\sINTEGER\sPRIMARY\sKEY\svalues\sshould\sbe\spreserved. +D 2022-09-05T15:56:09.391 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -390,9 +390,9 @@ F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f5974282 F ext/recover/recover1.test a848af8c82fe0731af835ff99475724f8654d2f24f772cc4e6f7ec4eb2ab71ea F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c F ext/recover/recoverold.test e7e00c78ec35b60488369ddf99e36a3b30e686566571969b05781e5063bdffe8 -F ext/recover/sqlite3recover.c 395c9f623cf84bd8c2e651ec112898d81e1908bbda66fe5f0efcfaa85ad2b262 -F ext/recover/sqlite3recover.h 35aacde3b3834d8ceefb20a2cf0ba221cbb5d802efc11a0529aafc018c462e13 -F ext/recover/test_recover.c 112c580e7cd765a20bbc94998f8b43b629db47fa6bfd696484ca722e418f4172 +F ext/recover/sqlite3recover.c 13e78c8a9d5521e06ebe5ac992a90169155e685f5c4b3cebc632c50b41e061c9 +F ext/recover/sqlite3recover.h ed34bc96befdf581a7de039d99772caabf0b338eb2a841d869acd9f7893ffce7 +F ext/recover/test_recover.c 9b8144ac94e6a2f3aabfcd24db6416179afdd32a3d654d1ef603c570e0384b2f F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 F ext/repair/checkfreelist.c e21f06995ff4efdc1622dcceaea4dcba2caa83ca2f31a1607b98a8509168a996 F ext/repair/checkindex.c 4383e4469c21e5b9ae321d0d63cec53e981af9d7a6564be6374f0eeb93dfc890 @@ -2005,8 +2005,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P dbd1f1efb349a9c8886e42b3f07d3f4c576924136f111558c7294d0a272e415a -R f1692b1a741d9ca9573411dcac80d98d +P 253e498f5200b8b3e2bc309587af108dd1cec8a884b3d2a49d5406525c9a4b4c +R e5ada01eb624b55d2f64c4994830b06d U dan -Z 9a22d599f360a7b8179802a74accdf31 +Z a7e3c99975f0369d94d22f82581262dd # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9852bbbed1..f34597368d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -253e498f5200b8b3e2bc309587af108dd1cec8a884b3d2a49d5406525c9a4b4c \ No newline at end of file +69cc9aba56a196bbd159bd24868aa5ccc60bed0dc612d09ed8a3ae898f156809 \ No newline at end of file From d5e3fe50f8f7d3aa01cf06ad7815dbf5bec1548f Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 5 Sep 2022 21:00:22 +0000 Subject: [PATCH 050/428] Add a mode to output SQL statements instead of populating a database to the recover extension. FossilOrigin-Name: 73058416e7da6581000898b7988a7010e2ce6632246f4c12b4398700c7744b83 --- ext/recover/recover1.test | 20 ++++++++ ext/recover/recoverold.test | 28 ++++++++++- ext/recover/sqlite3recover.c | 96 ++++++++++++++++++++++++++++-------- ext/recover/sqlite3recover.h | 12 ++++- ext/recover/test_recover.c | 44 +++++++++++++++-- manifest | 20 ++++---- manifest.uuid | 2 +- 7 files changed, 182 insertions(+), 40 deletions(-) diff --git a/ext/recover/recover1.test b/ext/recover/recover1.test index 73832c8a12..167b2796bd 100644 --- a/ext/recover/recover1.test +++ b/ext/recover/recover1.test @@ -52,8 +52,27 @@ proc do_recover_test {tn} { sqlite3 db2 test.db2 uplevel [list do_test $tn.2 [list compare_dbs db db2] {}] db2 close + + forcedelete test.db2 + forcedelete rstate.db + + uplevel [list do_test $tn.3 { + set ::sqlhook [list] + set R [sqlite3_recover_init_sql db main my_sql_hook] + $R config testdb rstate.db + $R step + $R finish + } {}] + + sqlite3 db2 test.db2 + execsql [join $::sqlhook ";"] db2 + uplevel [list do_test $tn.4 [list compare_dbs db db2] {}] + db2 close } +proc my_sql_hook {sql} { + lappend ::sqlhook $sql +} do_execsql_test 1.0 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b); @@ -112,6 +131,7 @@ do_execsql_test 7.1 { SELECT * FROM t2 } {10 11 ten} +breakpoint do_recover_test 7.2 finish_test diff --git a/ext/recover/recoverold.test b/ext/recover/recoverold.test index 8f70a7473d..83f210aebc 100644 --- a/ext/recover/recoverold.test +++ b/ext/recover/recoverold.test @@ -53,13 +53,37 @@ proc do_recover_test {tn {tsql {}} {res {}}} { sqlite3 db2 test.db2 if {$tsql==""} { - uplevel [list do_test $tn [list compare_dbs db db2] {}] + uplevel [list do_test $tn.1 [list compare_dbs db db2] {}] } else { - uplevel [list do_execsql_test -db db2 $tn $tsql $res] + uplevel [list do_execsql_test -db db2 $tn.1 $tsql $res] + } + db2 close + + forcedelete test.db2 + forcedelete rstate.db + + set ::sqlhook [list] + set R [sqlite3_recover_init_sql db main my_sql_hook] + $R config lostandfound lost_and_found + $R step + $R finish + + sqlite3 db2 test.db2 + db2 eval [join $::sqlhook ";"] + + if {$tsql==""} { + uplevel [list do_test $tn.sql [list compare_dbs db db2] {}] + } else { + uplevel [list do_execsql_test -db db2 $tn.sql $tsql $res] } db2 close } +proc my_sql_hook {sql} { + lappend ::sqlhook $sql +} + + set doc { hello world diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index e0eda09d82..1014e576a4 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -64,12 +64,6 @@ struct RecoverBitmap { ** */ #define RECOVERY_SCHEMA \ -" CREATE TABLE recovery.freelist(" \ -" pgno INTEGER PRIMARY KEY" \ -" );" \ -" CREATE TABLE recovery.dbptr(" \ -" pgno, child, PRIMARY KEY(child, pgno)" \ -" ) WITHOUT ROWID;" \ " CREATE TABLE recovery.map(" \ " pgno INTEGER PRIMARY KEY, parent INT" \ " );" \ @@ -96,6 +90,9 @@ struct sqlite3_recover { char *zLostAndFound; /* Name of lost-and-found table (or NULL) */ int bFreelistCorrupt; int bRecoverRowid; + + void *pSqlCtx; + int (*xSql)(void*,const char*); }; /* @@ -445,13 +442,6 @@ static int recoverOpenOutput(sqlite3_recover *p){ return rc; } -static int recoverCacheDbptr(sqlite3_recover *p){ - return recoverExec(p, p->dbOut, - "INSERT INTO recovery.dbptr " - "SELECT pgno, child FROM sqlite_dbptr('getpage()')" - ); -} - static int recoverCacheSchema(sqlite3_recover *p){ return recoverExec(p, p->dbOut, "WITH RECURSIVE pages(p) AS (" @@ -555,6 +545,15 @@ static void recoverAddTable(sqlite3_recover *p, const char *zName, i64 iRoot){ } } +static void recoverSqlCallback(sqlite3_recover *p, const char *zSql){ + if( p->errCode==SQLITE_OK && p->xSql ){ + int res = p->xSql(p->pSqlCtx, zSql); + if( res ){ + recoverError(p, SQLITE_ERROR, "callback returned an error - %d", res); + } + } +} + /* ** */ @@ -581,6 +580,7 @@ static int recoverWriteSchema1(sqlite3_recover *p){ int rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0); if( rc==SQLITE_OK ){ + recoverSqlCallback(p, zSql); if( bTable ){ if( SQLITE_ROW==sqlite3_step(pTblname) ){ const char *zName = sqlite3_column_text(pTblname, 0); @@ -614,6 +614,8 @@ static int recoverWriteSchema2(sqlite3_recover *p){ int rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0); if( rc!=SQLITE_OK && rc!=SQLITE_ERROR ){ recoverDbError(p, p->dbOut); + }else if( rc==SQLITE_OK ){ + recoverSqlCallback(p, zSql); } } } @@ -644,9 +646,12 @@ static sqlite3_stmt *recoverInsertStmt( int nField ){ const char *zSep = ""; + const char *zSqlSep = ""; char *zSql = 0; + char *zFinal = 0; char *zBind = 0; int ii; + int bSql = p->xSql ? 1 : 0; sqlite3_stmt *pRet = 0; assert( nField<=pTab->nCol ); @@ -656,9 +661,16 @@ static sqlite3_stmt *recoverInsertStmt( if( pTab->iRowidBind ){ assert( pTab->bIntkey ); zSql = recoverMPrintf(p, "%z_rowid_", zSql); - zBind = recoverMPrintf(p, "%z?%d", zBind, pTab->iRowidBind); + if( bSql ){ + zBind = recoverMPrintf(p, "%zquote(?%d)", zBind, pTab->iRowidBind); + }else{ + zBind = recoverMPrintf(p, "%z?%d", zBind, pTab->iRowidBind); + } + zSqlSep = "||', '||"; zSep = ", "; } + + for(ii=0; iiaCol[ii].eHidden; if( eHidden!=RECOVER_EHIDDEN_VIRTUAL @@ -666,14 +678,31 @@ static sqlite3_stmt *recoverInsertStmt( ){ assert( pTab->aCol[ii].iField>=0 && pTab->aCol[ii].iBind>=1 ); zSql = recoverMPrintf(p, "%z%s%Q", zSql, zSep, pTab->aCol[ii].zCol); - zBind = recoverMPrintf(p, "%z%s?%d", zBind, zSep, pTab->aCol[ii].iBind); + + if( bSql ){ + zBind = recoverMPrintf( + p, "%z%squote(?%d)", zBind, zSqlSep, pTab->aCol[ii].iBind + ); + zSqlSep = "||', '||"; + }else{ + zBind = recoverMPrintf(p, "%z%s?%d", zBind, zSep, pTab->aCol[ii].iBind); + } zSep = ", "; } } - zSql = recoverMPrintf(p, "%z) VALUES (%z)", zSql, zBind); - pRet = recoverPrepare(p, p->dbOut, zSql); + if( bSql ){ + zFinal = recoverMPrintf(p, "SELECT %Q || ') VALUES (' || %s || ')'", + zSql, zBind + ); + }else{ + zFinal = recoverMPrintf(p, "%s) VALUES (%s)", zSql, zBind); + } + + pRet = recoverPrepare(p, p->dbOut, zFinal); sqlite3_free(zSql); + sqlite3_free(zBind); + sqlite3_free(zFinal); return pRet; } @@ -744,6 +773,7 @@ static char *recoverLostAndFoundCreate( sqlite3_free(zField); recoverExec(p, p->dbOut, zSql); + recoverSqlCallback(p, zSql); sqlite3_free(zSql); }else if( p->errCode==SQLITE_OK ){ recoverError( @@ -838,6 +868,7 @@ static void recoverLostAndFoundPopulate( sqlite3_value_free(apVal[ii]); apVal[ii] = 0; } + sqlite3_clear_bindings(pInsert); bHaveRowid = 0; nVal = -1; } @@ -1052,7 +1083,10 @@ static int recoverWriteData(sqlite3_recover *p){ sqlite3_bind_int64(pInsert, pTab->iRowidBind, iRowid); } - sqlite3_step(pInsert); + if( SQLITE_ROW==sqlite3_step(pInsert) && p->xSql ){ + const char *zSql = (const char*)sqlite3_column_text(pInsert, 0); + recoverSqlCallback(p, zSql); + } recoverReset(p, pInsert); assert( p->errCode || pInsert ); if( pInsert ) sqlite3_clear_bindings(pInsert); @@ -1098,10 +1132,12 @@ static int recoverWriteData(sqlite3_recover *p){ return p->errCode; } -sqlite3_recover *sqlite3_recover_init( +sqlite3_recover *recoverInit( sqlite3* db, const char *zDb, - const char *zUri + const char *zUri, + int (*xSql)(void*, const char*), + void *pSqlCtx ){ sqlite3_recover *pRet = 0; int nDb = 0; @@ -1123,11 +1159,30 @@ sqlite3_recover *sqlite3_recover_init( pRet->zUri = &pRet->zDb[nDb+1]; memcpy(pRet->zDb, zDb, nDb); memcpy(pRet->zUri, zUri, nUri); + pRet->xSql = xSql; + pRet->pSqlCtx = pSqlCtx; } return pRet; } +sqlite3_recover *sqlite3_recover_init( + sqlite3* db, + const char *zDb, + const char *zUri +){ + return recoverInit(db, zDb, zUri, 0, 0); +} + +sqlite3_recover *sqlite3_recover_init_sql( + sqlite3* db, + const char *zDb, + int (*xSql)(void*, const char*), + void *pSqlCtx +){ + return recoverInit(db, zDb, "", xSql, pSqlCtx); +} + const char *sqlite3_recover_errmsg(sqlite3_recover *p){ return p ? p->zErrMsg : "not an error"; } @@ -1176,7 +1231,6 @@ static void recoverStep(sqlite3_recover *p){ if( p->dbOut==0 ){ if( recoverOpenOutput(p) ) return; - if( recoverCacheDbptr(p) ) return; if( recoverCacheSchema(p) ) return; if( recoverWriteSchema1(p) ) return; if( recoverWriteData(p) ) return; diff --git a/ext/recover/sqlite3recover.h b/ext/recover/sqlite3recover.h index 0294baddaa..5dc2a56d30 100644 --- a/ext/recover/sqlite3recover.h +++ b/ext/recover/sqlite3recover.h @@ -24,7 +24,8 @@ extern "C" { typedef struct sqlite3_recover sqlite3_recover; -/* Create an object to recover data from database zDb (e.g. "main") +/* +** Create an object to recover data from database zDb (e.g. "main") ** opened by handle db. Data will be recovered into the database ** identified by parameter zUri. Database zUri is clobbered if it ** already exists. @@ -35,6 +36,13 @@ sqlite3_recover *sqlite3_recover_init( const char *zUri ); +sqlite3_recover *sqlite3_recover_init_sql( + sqlite3* db, + const char *zDb, + int (*xSql)(void*, const char*), + void *pCtx +); + /* Details TBD. */ int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg); @@ -60,6 +68,8 @@ int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg); ** database, but often ends up "recovering" deleted records. ** ** SQLITE_RECOVER_ROWIDS: +** +** SQLITE_RECOVER_SQLHOOK: */ #define SQLITE_RECOVER_TESTDB 789 #define SQLITE_RECOVER_LOST_AND_FOUND 790 diff --git a/ext/recover/test_recover.c b/ext/recover/test_recover.c index 0646ff78b8..acfcf8c7a3 100644 --- a/ext/recover/test_recover.c +++ b/ext/recover/test_recover.c @@ -20,8 +20,31 @@ typedef struct TestRecover TestRecover; struct TestRecover { sqlite3_recover *p; + Tcl_Interp *interp; + Tcl_Obj *pScript; }; +static int xSqlCallback(void *pSqlArg, const char *zSql){ + TestRecover *p = (TestRecover*)pSqlArg; + Tcl_Obj *pEval = 0; + int res = 0; + + pEval = Tcl_DuplicateObj(p->pScript); + Tcl_IncrRefCount(pEval); + + res = Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zSql, -1)); + if( res==TCL_OK ){ + res = Tcl_EvalObjEx(p->interp, pEval, 0); + } + + Tcl_DecrRefCount(pEval); + if( res ){ + Tcl_BackgroundError(p->interp); + return TCL_ERROR; + } + return SQLITE_OK; +} + static int getDbPointer(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **pDb){ Tcl_CmdInfo info; if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(pObj), &info) ){ @@ -171,17 +194,26 @@ static int test_sqlite3_recover_init( const char *zDb = 0; const char *zUri = 0; char zCmd[128]; + int bSql = clientData ? 1 : 0; if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME URI"); + const char *zErr = (bSql ? "DB DBNAME SCRIPT" : "DB DBNAME URI"); + Tcl_WrongNumArgs(interp, 1, objv, zErr); return TCL_ERROR; } if( getDbPointer(interp, objv[1], &db) ) return TCL_ERROR; zDb = Tcl_GetString(objv[2]); - zUri = Tcl_GetString(objv[3]); pNew = ckalloc(sizeof(TestRecover)); - pNew->p = sqlite3_recover_init(db, zDb, zUri); + if( bSql==0 ){ + zUri = Tcl_GetString(objv[3]); + pNew->p = sqlite3_recover_init(db, zDb, zUri); + }else{ + pNew->interp = interp; + pNew->pScript = objv[3]; + Tcl_IncrRefCount(pNew->pScript); + pNew->p = sqlite3_recover_init_sql(db, zDb, xSqlCallback, (void*)pNew); + } sprintf(zCmd, "sqlite_recover%d", iTestRecoverCmd++); Tcl_CreateObjCommand(interp, zCmd, testRecoverCmd, (void*)pNew, 0); @@ -194,14 +226,16 @@ int TestRecover_Init(Tcl_Interp *interp){ struct Cmd { const char *zCmd; Tcl_ObjCmdProc *xProc; + void *pArg; } aCmd[] = { - { "sqlite3_recover_init", test_sqlite3_recover_init }, + { "sqlite3_recover_init", test_sqlite3_recover_init, 0 }, + { "sqlite3_recover_init_sql", test_sqlite3_recover_init, (void*)1 }, }; int i; for(i=0; izCmd, p->xProc, 0, 0); + Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, p->pArg, 0); } return TCL_OK; diff --git a/manifest b/manifest index 9f01e7c919..9244609811 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\sSQLITE_RECOVER_ROWIDS\soption.\sTo\sspecify\sthat\srowid\svalues\sthat\sare\snot\salso\sexplicit\sINTEGER\sPRIMARY\sKEY\svalues\sshould\sbe\spreserved. -D 2022-09-05T15:56:09.391 +C Add\sa\smode\sto\soutput\sSQL\sstatements\sinstead\sof\spopulating\sa\sdatabase\sto\sthe\srecover\sextension. +D 2022-09-05T21:00:22.268 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -387,12 +387,12 @@ F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2 F ext/rbu/sqlite3rbu.c 8737cabdfbee84bb25a7851ecef8b1312be332761238da9be6ddb10c62ad4291 F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812 F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a -F ext/recover/recover1.test a848af8c82fe0731af835ff99475724f8654d2f24f772cc4e6f7ec4eb2ab71ea +F ext/recover/recover1.test ae8ce9828210aa6c466bf88e23b0933849d5bb43091abe48cf2e56d636e51e93 F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c -F ext/recover/recoverold.test e7e00c78ec35b60488369ddf99e36a3b30e686566571969b05781e5063bdffe8 -F ext/recover/sqlite3recover.c 13e78c8a9d5521e06ebe5ac992a90169155e685f5c4b3cebc632c50b41e061c9 -F ext/recover/sqlite3recover.h ed34bc96befdf581a7de039d99772caabf0b338eb2a841d869acd9f7893ffce7 -F ext/recover/test_recover.c 9b8144ac94e6a2f3aabfcd24db6416179afdd32a3d654d1ef603c570e0384b2f +F ext/recover/recoverold.test f368a6ae2db12b6017257b332a19ab5df527f4061e43f12f5c85d8e2b236f074 +F ext/recover/sqlite3recover.c 47767b52f09fb1bba47009236285f09bcf68b6b5d1ec0de96b1a30b508e536a4 +F ext/recover/sqlite3recover.h 32f89b66f0235c0d94dfee0f1c3e9ed1ad726b3c4f3716ef0262b31cc33e8301 +F ext/recover/test_recover.c be0d74f0eba44fe7964e22d287dba0f3fa2baf197f630d51f0f9066af9b5eb2a F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 F ext/repair/checkfreelist.c e21f06995ff4efdc1622dcceaea4dcba2caa83ca2f31a1607b98a8509168a996 F ext/repair/checkindex.c 4383e4469c21e5b9ae321d0d63cec53e981af9d7a6564be6374f0eeb93dfc890 @@ -2005,8 +2005,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 253e498f5200b8b3e2bc309587af108dd1cec8a884b3d2a49d5406525c9a4b4c -R e5ada01eb624b55d2f64c4994830b06d +P 69cc9aba56a196bbd159bd24868aa5ccc60bed0dc612d09ed8a3ae898f156809 +R fe94b03379801a454a2e4362e6c6eea7 U dan -Z a7e3c99975f0369d94d22f82581262dd +Z bbf845b5205dd27415c5eec6b9356a28 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f34597368d..89f3b16ef4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -69cc9aba56a196bbd159bd24868aa5ccc60bed0dc612d09ed8a3ae898f156809 \ No newline at end of file +73058416e7da6581000898b7988a7010e2ce6632246f4c12b4398700c7744b83 \ No newline at end of file From 108c51b6ae731d76b5c77f4c4689d64c89401230 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 5 Sep 2022 21:22:35 +0000 Subject: [PATCH 051/428] Fix a problem with script mode and lost-and-found tables. FossilOrigin-Name: 09ec588d2fe24dd321e88318fe90a9ae912cbc73c8a2d59a10c821625dd12d9d --- ext/recover/sqlite3recover.c | 29 +++++++++++++++++++++++------ manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index 1014e576a4..f4bf5c0e4f 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -800,13 +800,24 @@ static sqlite3_stmt *recoverLostAndFoundInsert( const char *zSep = ""; sqlite3_stmt *pRet = 0; - for(ii=0; iixSql==0 ){ + for(ii=0; iidbOut, "INSERT INTO %s VALUES(%s)", zTab, zBind + ); + }else{ + const char *zSep = ""; + for(ii=0; iidbOut, "SELECT 'INSERT INTO %s VALUES(' || %s || ')'", zTab, zBind + ); } - pRet = recoverPreparePrintf( - p, p->dbOut, "INSERT INTO %s VALUES(%s)", zTab, zBind - ); sqlite3_free(zBind); return pRet; } @@ -860,7 +871,9 @@ static void recoverLostAndFoundPopulate( for(ii=0; iixSql ){ + recoverSqlCallback(p, sqlite3_column_text(pInsert, 0)); + } recoverReset(p, pInsert); /* Discard the accumulated row data */ @@ -1229,6 +1242,8 @@ static void recoverStep(sqlite3_recover *p){ assert( p->errCode==SQLITE_OK ); + recoverSqlCallback(p, "PRAGMA writable_schema = on"); + if( p->dbOut==0 ){ if( recoverOpenOutput(p) ) return; if( recoverCacheSchema(p) ) return; @@ -1237,6 +1252,8 @@ static void recoverStep(sqlite3_recover *p){ if( p->zLostAndFound && recoverLostAndFound(p) ) return; if( recoverWriteSchema2(p) ) return; } + + recoverSqlCallback(p, "PRAGMA writable_schema = off"); } int sqlite3_recover_step(sqlite3_recover *p){ diff --git a/manifest b/manifest index 9244609811..d0030bd28b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sa\smode\sto\soutput\sSQL\sstatements\sinstead\sof\spopulating\sa\sdatabase\sto\sthe\srecover\sextension. -D 2022-09-05T21:00:22.268 +C Fix\sa\sproblem\swith\sscript\smode\sand\slost-and-found\stables. +D 2022-09-05T21:22:35.859 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -390,7 +390,7 @@ F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f5974282 F ext/recover/recover1.test ae8ce9828210aa6c466bf88e23b0933849d5bb43091abe48cf2e56d636e51e93 F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c F ext/recover/recoverold.test f368a6ae2db12b6017257b332a19ab5df527f4061e43f12f5c85d8e2b236f074 -F ext/recover/sqlite3recover.c 47767b52f09fb1bba47009236285f09bcf68b6b5d1ec0de96b1a30b508e536a4 +F ext/recover/sqlite3recover.c 5dca1b3904cb028bef7348a5e0726a253c48f7fcd5da5ec687a44e4e665a24bf F ext/recover/sqlite3recover.h 32f89b66f0235c0d94dfee0f1c3e9ed1ad726b3c4f3716ef0262b31cc33e8301 F ext/recover/test_recover.c be0d74f0eba44fe7964e22d287dba0f3fa2baf197f630d51f0f9066af9b5eb2a F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 @@ -2005,8 +2005,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 69cc9aba56a196bbd159bd24868aa5ccc60bed0dc612d09ed8a3ae898f156809 -R fe94b03379801a454a2e4362e6c6eea7 +P 73058416e7da6581000898b7988a7010e2ce6632246f4c12b4398700c7744b83 +R 9dcaa53e272447eefaba97715f5d61ae U dan -Z bbf845b5205dd27415c5eec6b9356a28 +Z 82e767f579594364114308f585b07fed # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 89f3b16ef4..8ad6e97f6b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -73058416e7da6581000898b7988a7010e2ce6632246f4c12b4398700c7744b83 \ No newline at end of file +09ec588d2fe24dd321e88318fe90a9ae912cbc73c8a2d59a10c821625dd12d9d \ No newline at end of file From 09aa80d109eba3e3317a6d844673e6a0bc64ca06 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 6 Sep 2022 16:35:54 +0000 Subject: [PATCH 052/428] Minor internal doc clarifications. FossilOrigin-Name: 09796cc2bfce7bc4ce11db9673d20737259e9928f0484660cba3a9751f7d01c5 --- ext/wasm/api/sqlite3-api-oo1.js | 14 +++++++++----- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 3d24a7689a..aafc04a2d5 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -197,7 +197,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ Reminder: this will also fail after the statement is finalized but the resulting error will be about an out-of-bounds column - index. + index rather than a statement-is-finalized error. */ const affirmColIndex = function(stmt,ndx){ if((ndx !== (ndx|0)) || ndx<0 || ndx>=stmt.columnCount){ @@ -213,10 +213,14 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ { sql: the SQL, opt: 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. + The opt object is a normalized copy of any passed to this + function. The sql will be converted to a string if it is provided + in one of the supported non-string formats. + + cbArg is only set if the opt.callback or opt.resultRows are 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 = Object.create(null); diff --git a/manifest b/manifest index 8171ff2ddc..bfd4f4eb2e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\strunk\sinto\sfiddle-opfs\sbranch. -D 2022-09-06T09:59:06.910 +C Minor\sinternal\sdoc\sclarifications. +D 2022-09-06T16:35:54.448 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -483,7 +483,7 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 1a12e64060c2cb0defd34656a76a9b1d7ed58459c290249bb31567c806fd44de F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3 -F ext/wasm/api/sqlite3-api-oo1.js 8818c96272ed70e8a95d09b10f2381df94a79de0d722ef9057005e6594db2f33 +F ext/wasm/api/sqlite3-api-oo1.js b06a1ac982c7d433396b8304550ce1493a63671a3bf653c3b5646a9075e0ca41 F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db98a17685ab7a34a03953c7a F ext/wasm/api/sqlite3-api-worker1.js 73579555563b789785ae83724014eaf31811073aad9be6596c8336ffb51edd71 @@ -2015,8 +2015,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 4488cb5798f34cbba14eb8e2aa5da8420c14a85d37d278d357406aedd5c3a9e5 6b00ecb59fd303f7985902c35a46db9e729201d4beaedea46596b728d9e4b1c8 -R fe1e5247661b941e3cd5ee02be72aa3d +P 7f76eaec793720db87415a476ddf539bc4dea3e74c8e5406d6739206aebdacc2 +R ddf4f26b01c2c564d2354931a4072700 U stephan -Z 52c5dac4d2eba4046599a7e7052e5fe2 +Z d6b7c42fac31d9af4682922126e1ce62 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c528b411f9..dacbc05c13 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7f76eaec793720db87415a476ddf539bc4dea3e74c8e5406d6739206aebdacc2 \ No newline at end of file +09796cc2bfce7bc4ce11db9673d20737259e9928f0484660cba3a9751f7d01c5 \ No newline at end of file From 100b496dd28f62ff12f650c0eaf4827674c5e537 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 6 Sep 2022 16:47:43 +0000 Subject: [PATCH 053/428] Initial build of speedtest1.wasm and speedtest1.html with which to run it. FossilOrigin-Name: 4441535e3e54dc1881f700fa3878964eb8554a6790fd6aa32945f7cc104a8467 --- ext/wasm/GNUmakefile | 39 ++++++++++++++- ext/wasm/common/whwasmutil.js | 30 ++++++++++-- ext/wasm/speedtest1.html | 92 +++++++++++++++++++++++++++++++++++ manifest | 15 +++--- manifest.uuid | 2 +- 5 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 ext/wasm/speedtest1.html diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 11b1402662..ae9870054f 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -33,6 +33,10 @@ dir.jacc := jaccwabyt dir.common := common CLEAN_FILES := *~ $(dir.jacc)/*~ $(dir.api)/*~ $(dir.common)/*~ +sqlite3.c := $(dir.top)/sqlite3.c +$(sqlite3.c): + $(MAKE) -C $(dir.top) sqlite3.c + SQLITE_OPT = \ -DSQLITE_ENABLE_FTS4 \ -DSQLITE_ENABLE_RTREE \ @@ -53,8 +57,6 @@ SQLITE_OPT = \ # ^^^ MEMSYS5 is hypothetically useful for non-Emscripten builds but # requires adding more infrastructure and fixing one spot in the # sqlite3 internals which calls malloc() early on. -$(dir.top)/sqlite3.c: - $(MAKE) -C $(dir.top) sqlite3.c # SQLITE_OMIT_LOAD_EXTENSION: if this is true, sqlite3_vfs::xDlOpen # and friends may be NULL. @@ -299,6 +301,7 @@ wasm: $(sqlite3.js) # Bits for use with batch-runner.js... dir.sql := sql speedtest1 := ../../speedtest1 +speedtest1.c := ../../test/speedtest1.c speedtest1.sql := $(dir.sql)/speedtest1.sql $(speedtest1): $(MAKE) -C ../.. speedtest1 @@ -315,6 +318,38 @@ clean-batch: batch: batch-runner.list all: batch +speedtest1.js := speedtest1.js +$(speedtest1.js): emcc.cflags+= +$(speedtest1.js): $(speedtest1.c) $(MAKEFILE) + $(emcc.bin) -g $(emcc_opt) \ + -sINVOKE_RUN=0 \ + --no-entry \ + -sALLOW_TABLE_GROWTH \ + -sABORTING_MALLOC \ + -sINITIAL_MEMORY=128450560 \ + -sSTRICT_JS \ + -sENVIRONMENT=web \ + -sMODULARIZE \ + -sEXPORT_NAME=sqlite3Speedtest1InitModule \ + -Wno-limited-postlink-optimizations \ + -sEXPORTED_FUNCTIONS=_main,_malloc,_free \ + -sDYNAMIC_EXECUTION=0 \ + --minify 0 \ + -I. -I$(dir.top) \ + -DSQLITE_THREADSAFE=0 \ + -DSQLITE_TEMP_STORE=3 \ + -DSQLITE_OMIT_UTF16 \ + -DSQLITE_OMIT_DEPRECATED \ + -DSQLITE_OMIT_SHARED_CACHE \ + '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' \ + -DSQLITE_SPEEDTEST1_WASM \ + $(emcc_flags_wasmfs) \ + -o $@ $(speedtest1.c) $(sqlite3.c) -lm + +speedtest1: $(speedtest1.js) +all: $(speedtest1.js) +CLEAN_FILES += $(speedtest1.js) $(subst .js,.wasm,$(speedtest1.js)) + ######################################################################## # fiddle_remote is the remote destination for the fiddle app. It # must be a [user@]HOST:/path for rsync. diff --git a/ext/wasm/common/whwasmutil.js b/ext/wasm/common/whwasmutil.js index 38adc76c2f..f72fba72c7 100644 --- a/ext/wasm/common/whwasmutil.js +++ b/ext/wasm/common/whwasmutil.js @@ -212,9 +212,10 @@ self.WhWasmUtilInstaller = function(target){ that will certainly change. */ const ptrIR = target.pointerIR || 'i32'; - const ptrSizeof = ('i32'===ptrIR ? 4 - : ('i64'===ptrIR - ? 8 : toss("Unhandled ptrSizeof:",ptrIR))); + const ptrSizeof = target.ptrSizeof = + ('i32'===ptrIR ? 4 + : ('i64'===ptrIR + ? 8 : toss("Unhandled ptrSizeof:",ptrIR))); /** Stores various cached state. */ const cache = Object.create(null); /** Previously-recorded size of cache.memory.buffer, noted so that @@ -1021,6 +1022,29 @@ self.WhWasmUtilInstaller = function(target){ (jstr, returnWithLength=false)=>__allocCStr(jstr, returnWithLength, target.scopedAlloc, 'scopedAllocCString()'); + /** + Creates an array, using scopedAlloc(), suitable for passing to a + C-level main() routine. The input is a collection with a length + property and a forEach() method. A block of memory list.length + entries long is allocated and each pointer-sized block of that + memory is populated with a scopedAllocCString() conversion of the + (''+value) of each element. Returns a pointer to the start of the + list, suitable for passing as the 2nd argument to a C-style + main() function. + + Throws if list.length is falsy or scopedAllocPush() is not active. + */ + target.scopedAllocMainArgv = function(list){ + if(!list.length) toss("Cannot allocate empty array."); + const pList = target.scopedAlloc(list.length * target.ptrSizeof); + let i = 0; + list.forEach((e)=>{ + target.setPtrValue(pList + (target.ptrSizeof * i++), + target.scopedAllocCString(""+e)); + }); + return pList; + }; + /** Wraps function call func() in a scopedAllocPush() and scopedAllocPop() block, such that all calls to scopedAlloc() and diff --git a/ext/wasm/speedtest1.html b/ext/wasm/speedtest1.html new file mode 100644 index 0000000000..526c05ac46 --- /dev/null +++ b/ext/wasm/speedtest1.html @@ -0,0 +1,92 @@ + + + + + + + + + speedtest1.wasm + + +

speedtest1.wasm
+ +
+
+
Initializing app...
+
+ 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. +
+
+
Downloading...
+
+ +
+
Output is sent to the dev console because we cannot update the UI while the + speedtest is running unless/until we move the speedtest to a worker thread.
+
+
+ + + + + + diff --git a/manifest b/manifest index bfd4f4eb2e..91cc5e9d1c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\sinternal\sdoc\sclarifications. -D 2022-09-06T16:35:54.448 +C Initial\sbuild\sof\sspeedtest1.wasm\sand\sspeedtest1.html\swith\swhich\sto\srun\sit. +D 2022-09-06T16:47:43.072 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 49d76f7b4c45d6ff8cb573ba0d1dd7b4712e59582bdc1dfb2f96a4b89f47e34c +F ext/wasm/GNUmakefile 445b81c44804f029bcc938aa777ca3fe450a0061a586a21be411362575c7cf61 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -494,7 +494,7 @@ F ext/wasm/batch-runner.js a727cbbffe63fd17fb5a590dc679f0b13bd51880e8f84b461d7df F ext/wasm/common/SqliteTestUtil.js 7a543e238c2ebda922c85076abda017d0480944fdfee576692a0c3a580319ebd F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 -F ext/wasm/common/whwasmutil.js 3d68a9cd87a2d8f7e6d5a78f3b829a927f2e28422685e029139f6e75d4ed42e4 +F ext/wasm/common/whwasmutil.js ed10ab537b800e1f8fac9d5ecec00908f4d4b0e1a270de196e43782600ae59f7 F ext/wasm/demo-oo1.html 75646855b38405d82781246fd08c852a2b3bee05dd9f0fe10ab655a8cffb79aa F ext/wasm/demo-oo1.js aad38cb90b6fa7fd4d1184e759b25056fb4ed45c4957c458896354281259515f F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f @@ -510,6 +510,7 @@ F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb43 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0 +F ext/wasm/speedtest1.html e5b6ca8d09e15ab77fcec95ab30a330cc5376795f3d3b7cc05b9377b08824071 F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 @@ -2015,8 +2016,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 7f76eaec793720db87415a476ddf539bc4dea3e74c8e5406d6739206aebdacc2 -R ddf4f26b01c2c564d2354931a4072700 +P 09796cc2bfce7bc4ce11db9673d20737259e9928f0484660cba3a9751f7d01c5 +R 98ebaeee264c8d9d604307fc8843d5b6 U stephan -Z d6b7c42fac31d9af4682922126e1ce62 +Z 0a749029dbb2383547531131a6904c9b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index dacbc05c13..11b9ffd031 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -09796cc2bfce7bc4ce11db9673d20737259e9928f0484660cba3a9751f7d01c5 \ No newline at end of file +4441535e3e54dc1881f700fa3878964eb8554a6790fd6aa32945f7cc104a8467 \ No newline at end of file From 497b3e6a1152e58be842fa17dee247360aaf8888 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 6 Sep 2022 19:38:06 +0000 Subject: [PATCH 054/428] Tests and a fix for the SQLITE_RECOVER_ROWIDS option. FossilOrigin-Name: 1d5000f5718004110776ff58284d824854a93fe1cd1f6d8a4e8b8b0c2b2c3076 --- ext/recover/recoverrowid.test | 58 +++++++++++++++++++++++++++++++++++ ext/recover/sqlite3recover.c | 18 ++++++++--- manifest | 13 ++++---- manifest.uuid | 2 +- 4 files changed, 79 insertions(+), 12 deletions(-) create mode 100644 ext/recover/recoverrowid.test diff --git a/ext/recover/recoverrowid.test b/ext/recover/recoverrowid.test new file mode 100644 index 0000000000..3dfafbcc7f --- /dev/null +++ b/ext/recover/recoverrowid.test @@ -0,0 +1,58 @@ +# 2022 September 07 +# +# 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. +# +#*********************************************************************** +# +# Tests for the SQLITE_RECOVER_ROWIDS option. +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] recover_common.tcl] +source $testdir/tester.tcl +set testprefix recoverrowid + +ifcapable !vtab { + finish_test; return +} + +proc recover {db bRowids output} { + forcedelete $output + + set R [sqlite3_recover_init db main test.db2] + $R config rowids $bRowids + $R step + $R finish +} + +do_execsql_test 1.0 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 1), (2, 2), (3, 3), (4, 4); + DELETE FROM t1 WHERE a IN (1, 3); +} + +do_test 1.1 { + recover db 0 test.db2 + sqlite3 db2 test.db2 + execsql { SELECT rowid, a, b FROM t1 ORDER BY rowid} db2 +} {1 2 2 2 4 4} + +do_test 1.2 { + db2 close + recover db 1 test.db2 + sqlite3 db2 test.db2 + execsql { SELECT rowid, a, b FROM t1 ORDER BY rowid} db2 +} {2 2 2 4 4 4} +db2 close + + + + +finish_test diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index f4bf5c0e4f..df6ab36b98 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -403,14 +403,22 @@ static int recoverOpenOutput(sqlite3_recover *p){ sqlite3_free(zSql); } - + /* Truncate the output database. This is done by opening a new, empty, + ** temp db, then using the backup API to clobber any existing output db + ** with a copy of it. */ if( rc==SQLITE_OK ){ - sqlite3_backup *pBackup = sqlite3_backup_init(db, "main", db, "recovery"); - if( pBackup ){ - while( sqlite3_backup_step(pBackup, 1000)==SQLITE_OK ); - rc = sqlite3_backup_finish(pBackup); + sqlite3 *db2 = 0; + rc = sqlite3_open("", &db2); + if( rc==SQLITE_OK ){ + sqlite3_backup *pBackup = sqlite3_backup_init(db, "main", db2, "main"); + if( pBackup ){ + while( sqlite3_backup_step(pBackup, 1000)==SQLITE_OK ); + rc = sqlite3_backup_finish(pBackup); + } } + sqlite3_close(db2); } + if( rc==SQLITE_OK ){ rc = sqlite3_exec(db, RECOVERY_SCHEMA, 0, 0, 0); } diff --git a/manifest b/manifest index d0030bd28b..a19ce57ac1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\swith\sscript\smode\sand\slost-and-found\stables. -D 2022-09-05T21:22:35.859 +C Tests\sand\sa\sfix\sfor\sthe\sSQLITE_RECOVER_ROWIDS\soption. +D 2022-09-06T19:38:06.927 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -390,7 +390,8 @@ F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f5974282 F ext/recover/recover1.test ae8ce9828210aa6c466bf88e23b0933849d5bb43091abe48cf2e56d636e51e93 F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c F ext/recover/recoverold.test f368a6ae2db12b6017257b332a19ab5df527f4061e43f12f5c85d8e2b236f074 -F ext/recover/sqlite3recover.c 5dca1b3904cb028bef7348a5e0726a253c48f7fcd5da5ec687a44e4e665a24bf +F ext/recover/recoverrowid.test ec4436cd69e6cdacb48dd2963ff6dd9dbd5fe648376de5e7c0c2f4f6cbacb417 +F ext/recover/sqlite3recover.c 297fdef9da8523ef7fa3f88adb31356340826dac56c1fb13db2dd3e167806efe F ext/recover/sqlite3recover.h 32f89b66f0235c0d94dfee0f1c3e9ed1ad726b3c4f3716ef0262b31cc33e8301 F ext/recover/test_recover.c be0d74f0eba44fe7964e22d287dba0f3fa2baf197f630d51f0f9066af9b5eb2a F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 @@ -2005,8 +2006,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 73058416e7da6581000898b7988a7010e2ce6632246f4c12b4398700c7744b83 -R 9dcaa53e272447eefaba97715f5d61ae +P 09ec588d2fe24dd321e88318fe90a9ae912cbc73c8a2d59a10c821625dd12d9d +R 2900e793f9b609ad9d5a7b230af64f99 U dan -Z 82e767f579594364114308f585b07fed +Z a6081cceac978cdb593b5cd251ae6a63 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8ad6e97f6b..c0fbb47278 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -09ec588d2fe24dd321e88318fe90a9ae912cbc73c8a2d59a10c821625dd12d9d \ No newline at end of file +1d5000f5718004110776ff58284d824854a93fe1cd1f6d8a4e8b8b0c2b2c3076 \ No newline at end of file From 8fc8b5b35f8d576f784ef42c73bf498aa12505a2 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 6 Sep 2022 20:17:15 +0000 Subject: [PATCH 055/428] Get speedtest1.js working with WASMFS/OPFS. FossilOrigin-Name: 40e60f570d4f489d58d12e27c1c067b41d6c5a5e374c5fce0baa8881ef183216 --- ext/wasm/GNUmakefile | 68 ++++++++++++++++++++++------------- ext/wasm/api/sqlite3-wasm.c | 2 +- ext/wasm/common/whwasmutil.js | 2 +- ext/wasm/speedtest1.html | 68 ++++++++++++++++++++++++++--------- manifest | 18 +++++----- manifest.uuid | 2 +- 6 files changed, 107 insertions(+), 53 deletions(-) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index ae9870054f..09b4479ba5 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -259,10 +259,11 @@ emcc.jsflags += -sMEMORY64=0 # sqlite3.worker.js generated in conjunction with -sWASMFS). sqlite3.js := sqlite3.js sqlite3.wasm := sqlite3.wasm -$(dir.api)/sqlite3-wasm.o: emcc.cflags += $(SQLITE_OPT) -$(dir.api)/sqlite3-wasm.o: $(dir.top)/sqlite3.c +sqlite3-wasm.o := $(dir.api)/sqlite3-wasm.o +$(sqlite3-wasm.o): emcc.cflags += $(SQLITE_OPT) +$(sqlite3-wasm.o): $(dir.top)/sqlite3.c $(dir.api)/wasm_util.o: emcc.cflags += $(SQLITE_OPT) -sqlite3.wasm.c := $(dir.api)/sqlite3-wasm.c \ +sqlite3-wasm.c := $(dir.api)/sqlite3-wasm.c \ $(dir.jacc)/jaccwabyt_test.c # ^^^ FIXME (how?): jaccwabyt_test.c is only needed for the test apps, # so we don't really want to include it in release builds. However, we @@ -270,7 +271,7 @@ sqlite3.wasm.c := $(dir.api)/sqlite3-wasm.c \ # elide that file in release builds. That component is critical to the # VFS bindings so needs to be tested along with the core APIs. ifneq (,$(filter -sWASMFS,$(emcc.jsflags))) - $(dir.api)/sqlite3-wasm.o: emcc.cflags+=-DSQLITE_WASM_OPFS + $(sqlite3-wasm.o): emcc.cflags+=-DSQLITE_WASM_OPFS endif define WASM_C_COMPILE $(1).o := $$(subst .c,.o,$(1)) @@ -279,7 +280,7 @@ $$($(1).o): $$(MAKEFILE) $(1) $$(emcc.bin) $$(emcc_opt) $$(emcc.flags) $$(emcc.cflags) -c $(1) -o $$@ CLEAN_FILES += $$($(1).o) endef -$(foreach c,$(sqlite3.wasm.c),$(eval $(call WASM_C_COMPILE,$(c)))) +$(foreach c,$(sqlite3-wasm.c),$(eval $(call WASM_C_COMPILE,$(c)))) $(sqlite3.js): $(sqlite3.js): $(MAKEFILE) $(sqlite3.wasm.obj) \ EXPORTED_FUNCTIONS.api \ @@ -298,7 +299,7 @@ wasm: $(sqlite3.js) ######################################################################## ######################################################################## -# Bits for use with batch-runner.js... +# batch-runner.js... dir.sql := sql speedtest1 := ../../speedtest1 speedtest1.c := ../../test/speedtest1.c @@ -317,24 +318,38 @@ clean-batch: # pieces each time is an unnecessary time sink. batch: batch-runner.list all: batch +# end batch-runner.js +######################################################################## +# speedtest1.js... +emcc.speedtest1-flags := -g $(emcc_opt) +ifneq (0,$(ENABLE_WASMFS)) + emcc.speedtest1-flags += -pthread -sWASMFS -sPTHREAD_POOL_SIZE=2 + emcc.speedtest1-flags += -DSQLITE_WASM_OPFS +endif +emcc.speedtest1-flags += -sINVOKE_RUN=0 +#emcc.speedtest1-flags += --no-entry +emcc.speedtest1-flags += -flto +emcc.speedtest1-flags += -sABORTING_MALLOC +emcc.speedtest1-flags += -sINITIAL_MEMORY=128450560 +emcc.speedtest1-flags += -sSTRICT_JS +emcc.speedtest1-flags += $(emcc.environment) +emcc.speedtest1-flags += -sMODULARIZE +emcc.speedtest1-flags += -sEXPORT_NAME=sqlite3Speedtest1InitModule +emcc.speedtest1-flags += -Wno-limited-postlink-optimizations +emcc.speedtest1-flags += -sEXPORTED_FUNCTIONS=_main,_malloc,_free,_sqlite3_wasm_vfs_unlink,_sqlite3_wasm_init_opfs +emcc.speedtest1-flags += -sDYNAMIC_EXECUTION=0 +emcc.speedtest1-flags += --minify 0 speedtest1.js := speedtest1.js +speedtest1.wasm := $(subst .js,.wasm,$(speedtest1.js)) $(speedtest1.js): emcc.cflags+= -$(speedtest1.js): $(speedtest1.c) $(MAKEFILE) - $(emcc.bin) -g $(emcc_opt) \ - -sINVOKE_RUN=0 \ - --no-entry \ - -sALLOW_TABLE_GROWTH \ - -sABORTING_MALLOC \ - -sINITIAL_MEMORY=128450560 \ - -sSTRICT_JS \ - -sENVIRONMENT=web \ - -sMODULARIZE \ - -sEXPORT_NAME=sqlite3Speedtest1InitModule \ - -Wno-limited-postlink-optimizations \ - -sEXPORTED_FUNCTIONS=_main,_malloc,_free \ - -sDYNAMIC_EXECUTION=0 \ - --minify 0 \ +# speedtest1 notes re. sqlite3-wasm.o vs sqlite3-wasm.c: building against +# the latter (predictably) results in a slightly faster binary, but we're +# close enough to the target speed requirements that the 500ms makes a +# difference. +$(speedtest1.js): $(speedtest1.c) $(sqlite3-wasm.c) $(MAKEFILE) + $(emcc.bin) \ + $(emcc.speedtest1-flags) \ -I. -I$(dir.top) \ -DSQLITE_THREADSAFE=0 \ -DSQLITE_TEMP_STORE=3 \ @@ -343,13 +358,16 @@ $(speedtest1.js): $(speedtest1.c) $(MAKEFILE) -DSQLITE_OMIT_SHARED_CACHE \ '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' \ -DSQLITE_SPEEDTEST1_WASM \ - $(emcc_flags_wasmfs) \ - -o $@ $(speedtest1.c) $(sqlite3.c) -lm + -o $@ $(speedtest1.c) $(sqlite3-wasm.c) -lm +ifneq (,$(wasm-strip)) + $(wasm-strip) $(speedtest1.wasm) +endif + ls -la $@ $(speedtest1.wasm) speedtest1: $(speedtest1.js) all: $(speedtest1.js) -CLEAN_FILES += $(speedtest1.js) $(subst .js,.wasm,$(speedtest1.js)) - +CLEAN_FILES += $(speedtest1.js) $(speedtest1.wasm) +# end speedtest1.js ######################################################################## # fiddle_remote is the remote destination for the fiddle app. It # must be a [user@]HOST:/path for rsync. diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index 070282b8ee..cea6dffa1f 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -473,7 +473,7 @@ int sqlite3_wasm_init_opfs(const char *zMountPoint){ hypothetically suffice for the transient wasm-based virtual filesystem we're currently running in. */ const int rc = wasmfs_create_directory(zMountPoint, 0777, pOpfs); - emscripten_console_logf("OPFS mkdir rc=%d", rc); + emscripten_console_logf("OPFS mkdir(%s) rc=%d", zMountPoint, rc); if(rc) return SQLITE_IOERR; } return pOpfs ? 0 : SQLITE_NOMEM; diff --git a/ext/wasm/common/whwasmutil.js b/ext/wasm/common/whwasmutil.js index f72fba72c7..42f602d00d 100644 --- a/ext/wasm/common/whwasmutil.js +++ b/ext/wasm/common/whwasmutil.js @@ -1482,7 +1482,7 @@ self.WhWasmUtilInstaller = function(target){ if(Array.isArray(arguments[3])) args = arguments[3]; return target.xWrap(fname, resultType, argTypes||[]).apply(null, args||[]); }; - + return target; }; diff --git a/ext/wasm/speedtest1.html b/ext/wasm/speedtest1.html index 526c05ac46..5e05feed26 100644 --- a/ext/wasm/speedtest1.html +++ b/ext/wasm/speedtest1.html @@ -31,10 +31,37 @@ - - diff --git a/ext/wasm/common/testing.css b/ext/wasm/common/testing.css index 09c570f48a..e112fd0a83 100644 --- a/ext/wasm/common/testing.css +++ b/ext/wasm/common/testing.css @@ -1,3 +1,8 @@ +body { + display: flex; + flex-direction: column; + flex-wrap: wrap; +} textarea { font-family: monospace; } @@ -29,4 +34,17 @@ span.labeled-input { color: red; background-color: yellow; } -#test-output { font-family: monospace } +.warning { color: firebrick; } +.input-wrapper { white-space: nowrap; } +#test-output { + border: 1px inset; + padding: 0.25em; + /*max-height: 30em;*/ + overflow: auto; + white-space: break-spaces; + display: flex; flex-direction: column; + font-family: monospace; +} +#test-output.reverse { + flex-direction: column-reverse; +} diff --git a/ext/wasm/index.html b/ext/wasm/index.html new file mode 100644 index 0000000000..def70cce03 --- /dev/null +++ b/ext/wasm/index.html @@ -0,0 +1,54 @@ + + + + + + + + sqlite3 WASM Testing Page Index + + +
sqlite3 WASM test pages
+
+
Below is the list of test pages for the sqlite3 WASM + builds. All of them require that this directory have been + "make"d first. The intent is that this page be run + using:
+
althttpd -page index.html
+
and the individual tests be started in their own tab.
+
Warnings and Caveats: +
    +
  • Some of these pages require that + the web server emit the so-called COOP and COEP headers. The + default build of althttpd does not. +
  • +
  • Whether or not WASMFS/OPFS support is enabled on any given + page may depend on build-time options which are off by + default because they currently (as of 2022-09-08) break + with Worker-based pages. +
  • +
+
+
The tests... +
    +
  • testing1: sanity tests of the core APIs and surrounding utility code.
  • +
  • testing2: Worker-based test of OO API #1.
  • +
  • testing-worker1-promiser: + tests for the Promise-based wrapper of the Worker-based API.
  • +
  • batch-runner: runs batches of SQL exported from speedtest1.
  • +
  • speedtest1: a main-thread WASM build of speedtest1.
  • +
  • speedtest1-worker: an interactive Worker-thread variant of speedtest1.
  • +
  • demo-oo1: demonstration of the OO API #1.
  • + +
+
+ + + + diff --git a/ext/wasm/speedtest1-worker.html b/ext/wasm/speedtest1-worker.html new file mode 100644 index 0000000000..60c475798c --- /dev/null +++ b/ext/wasm/speedtest1-worker.html @@ -0,0 +1,315 @@ + + + + + + + + + speedtest1.wasm Worker + + +
speedtest1.wasm Worker
+ + +
+
+
Initializing app...
+
+ 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. +
+
+
Downloading...
+
+ +
+ +
+ + + + +
+
+ + + + diff --git a/ext/wasm/speedtest1-worker.js b/ext/wasm/speedtest1-worker.js new file mode 100644 index 0000000000..8512bdbbf7 --- /dev/null +++ b/ext/wasm/speedtest1-worker.js @@ -0,0 +1,99 @@ +'use strict'; +(function(){ + importScripts('common/whwasmutil.js','speedtest1.js'); + /** + If this environment contains OPFS, this function initializes it and + returns the name of the dir on which OPFS is mounted, else it returns + an empty string. + */ + const opfsDir = function f(wasmUtil){ + if(undefined !== f._) return f._; + const pdir = '/persistent'; + if( !self.FileSystemHandle + || !self.FileSystemDirectoryHandle + || !self.FileSystemFileHandle){ + return f._ = ""; + } + try{ + if(0===wasmUtil.xCallWrapped( + 'sqlite3_wasm_init_opfs', 'i32', ['string'], pdir + )){ + return f._ = pdir; + }else{ + return f._ = ""; + } + }catch(e){ + // sqlite3_wasm_init_opfs() is not available + return f._ = ""; + } + }; + opfsDir._ = undefined; + + const mPost = function(msgType,payload){ + postMessage({type: msgType, data: payload}); + }; + + const App = Object.create(null); + App.logBuffer = []; + const logMsg = (type,msgArgs)=>{ + const msg = msgArgs.join(' '); + App.logBuffer.push(msg); + mPost(type,msg); + }; + const log = (...args)=>logMsg('stdout',args); + const logErr = (...args)=>logMsg('stderr',args); + + const runSpeedtest = function(cliFlagsArray){ + const scope = App.wasm.scopedAllocPush(); + const dbFile = 0 ? "" : App.pDir+"/speedtest1.db"; + try{ + const argv = [ + "speedtest1.wasm", ...cliFlagsArray, dbFile + ]; + App.logBuffer.length = 0; + mPost('run-start', [...argv]); + App.wasm.xCall('__main_argc_argv', argv.length, + App.wasm.scopedAllocMainArgv(argv)); + }catch(e){ + mPost('error',e.message); + }finally{ + App.wasm.scopedAllocPop(scope); + App.unlink(dbFile); + mPost('run-end', App.logBuffer.join('\n')); + App.logBuffer.length = 0; + } + }; + + self.onmessage = function(msg){ + msg = msg.data; + switch(msg.type){ + case 'run': runSpeedtest(msg.data || []); break; + default: + logErr("Unhandled worker message type:",msg.type); + break; + } + }; + + const EmscriptenModule = { + print: log, + printErr: logErr, + setStatus: (text)=>mPost('load-status',text) + }; + self.sqlite3Speedtest1InitModule(EmscriptenModule).then(function(EmscriptenModule){ + log("Module inited."); + App.wasm = { + exports: EmscriptenModule.asm, + alloc: (n)=>EmscriptenModule._malloc(n), + dealloc: (m)=>EmscriptenModule._free(m), + memory: EmscriptenModule.asm.memory || EmscriptenModule.wasmMemory + }; + //console.debug('wasm =',wasm); + self.WhWasmUtilInstaller(App.wasm); + App.unlink = App.wasm.xWrap("sqlite3_wasm_vfs_unlink", "int", ["string"]); + App.pDir = opfsDir(App.wasm); + if(App.pDir){ + log("Persistent storage:",pDir); + } + mPost('ready',true); + }); +})(); diff --git a/ext/wasm/speedtest1.html b/ext/wasm/speedtest1.html index 5e05feed26..fad2b28129 100644 --- a/ext/wasm/speedtest1.html +++ b/ext/wasm/speedtest1.html @@ -10,6 +10,7 @@
speedtest1.wasm
+
@@ -24,6 +25,9 @@
+
This page starts running the main exe when it loads, which will + block the UI until it finishes! Adding UI controls to manually configure and start it + are TODO.
Output is sent to the dev console because we cannot update the UI while the speedtest is running unless/until we move the speedtest to a worker thread.

diff --git a/manifest b/manifest index aef6da12d0..75eb7e14a6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sa\snote\sabout\sEmscripten's\s-sSINGLE_FILE\sflag,\swhy\sit\swould\sbe\snice,\sand\swhy\swe\scan't\suse\sit. -D 2022-09-06T23:04:51.823 +C Add\sspeedtest1-worker.html,\san\sinteractive\sWorker-thread\svariant\sof\sspeedtest1.html.\sAdd\sext/wasm/index.html\sto\sact\sas\sa\sgateway\sto\sthe\svarious\stest\spages. +D 2022-09-08T15:30:59.731 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -489,11 +489,11 @@ F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db F ext/wasm/api/sqlite3-api-worker1.js 73579555563b789785ae83724014eaf31811073aad9be6596c8336ffb51edd71 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 19c3797edc35821e362a8b60ce45d1adfe6d24fca7cd1f55f89d2086ef33870e -F ext/wasm/batch-runner.html 2d44d99a556c46f586d3319003dd281dd0eb6a13eeadde3eab05ba81eec9ff8a +F ext/wasm/batch-runner.html 23209ade7981acce7ecd79d6eff9f4c5a4e8b14ae867ac27cd89b230be640fa6 F ext/wasm/batch-runner.js a727cbbffe63fd17fb5a590dc679f0b13bd51880e8f84b461d7df246417689e8 F ext/wasm/common/SqliteTestUtil.js 7a543e238c2ebda922c85076abda017d0480944fdfee576692a0c3a580319ebd F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f -F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 +F ext/wasm/common/testing.css 3a5143699c2b73a85b962271e1a9b3241b30d90e30d895e4f55665e648572962 F ext/wasm/common/whwasmutil.js f7282ef36c9625330d4e6e82d1beec6678cd101e95e7108cd85db587a788c145 F ext/wasm/demo-oo1.html 75646855b38405d82781246fd08c852a2b3bee05dd9f0fe10ab655a8cffb79aa F ext/wasm/demo-oo1.js aad38cb90b6fa7fd4d1184e759b25056fb4ed45c4957c458896354281259515f @@ -501,6 +501,7 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 +F ext/wasm/index.html 4f635f986dbc7518280abe0ef537ba41682e35f160fac35a0745cf6c4d223b62 F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f @@ -510,7 +511,9 @@ F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb43 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0 -F ext/wasm/speedtest1.html a1204f5cbbd592baa191535dc7eaa2d875f661aefb2a70a4631df0a925e19f4b +F ext/wasm/speedtest1-worker.html 4f8d7391ec17b6fd13f1a1e181436d3331842548fe7cc84e4e322f72850eb97b +F ext/wasm/speedtest1-worker.js 356b9953add4449acf199793db9b76b11ee016021918d8daffd19f08ec68d305 +F ext/wasm/speedtest1.html 02d281c25a8048cce24695bed01ca613d11f40b599cd47f3a18dca8c982d0a15 F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 @@ -1496,7 +1499,7 @@ F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715 F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d1f0c -F test/speedtest1.c 995c78f884e2388106b2b42de9e5a527e7cac01384b8f317ca0cc7b0814c9a18 +F test/speedtest1.c 55ef13e008cbb7dbea032478e60686599679b0b6192e44845c100fdabd14f8ff F test/spellfix.test 951a6405d49d1a23d6b78027d3877b4a33eeb8221dcab5704b499755bb4f552e F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3 F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33 @@ -2016,8 +2019,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 40e60f570d4f489d58d12e27c1c067b41d6c5a5e374c5fce0baa8881ef183216 -R 0096ecafc15630b25658c12eafbbea1b +P 5ea0623630d769a8f3f07a40cd119be86b631192cdb5178131876b01b40ee5e0 +R b5dc0101db445e11af3bccae9adaef48 U stephan -Z 7b5e51ab80c88507303a95f3b8f5d0e1 +Z 2c78924308cfac29c905a5ead30d4718 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 08a78c3d74..9c75a6fdf6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5ea0623630d769a8f3f07a40cd119be86b631192cdb5178131876b01b40ee5e0 \ No newline at end of file +f16c68ee6d5ebb8dec2ab656dbab2ddb5f1d5133153ad553f986b31020adaa38 \ No newline at end of file diff --git a/test/speedtest1.c b/test/speedtest1.c index 401217f105..3b8bd92e3b 100644 --- a/test/speedtest1.c +++ b/test/speedtest1.c @@ -2204,6 +2204,12 @@ int main(int argc, char **argv){ int i; /* Loop counter */ int rc; /* API return code */ +#ifdef SQLITE_SPEEDTEST1_WASM + /* Resetting all state is important for the WASM build, which may + ** call main() multiple times. */ + memset(&g, 0, sizeof(g)); + iTestNumber = 0; +#endif #ifdef SQLITE_CKSUMVFS_STATIC sqlite3_register_cksumvfs(0); #endif From cb94132d9f7997bc1b0fbce97740c1576cac40a6 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 8 Sep 2022 15:48:01 +0000 Subject: [PATCH 061/428] Code snapshot. Completely untested. Probably does not compile. FossilOrigin-Name: 51e267696d25f2f83b5bb847d5254ab15573ef1a16ea3d7dd8279c73c0bee539 --- ext/misc/vfskv.c | 241 +++++++++++++++++++++++++++++++++++++++++------ manifest | 15 ++- manifest.uuid | 2 +- 3 files changed, 217 insertions(+), 41 deletions(-) diff --git a/ext/misc/vfskv.c b/ext/misc/vfskv.c index 4d8d797a4f..0773f70945 100644 --- a/ext/misc/vfskv.c +++ b/ext/misc/vfskv.c @@ -47,19 +47,26 @@ static int kvstorageRead(KVStorage*, const char *zKey, char *zBuf, int nBuf); typedef struct KVVfsVfs KVVfsVfs; typedef struct KVVfsFile KVVfsFile; + +/* All information about the database */ struct KVVfsVfs { sqlite3_vfs base; /* VFS methods */ KVStorage *pStore; /* Single command KV storage object */ KVVfsFile *pFiles; /* List of open KVVfsFile objects */ }; +/* A single open file. There are only two files represented by this +** VFS - the database and the rollback journal. +*/ struct KVVfsFile { sqlite3_file base; /* IO methods */ KVVfsVfs *pVfs; /* The VFS to which this file belongs */ KVVfsFile *pNext; /* Next in list of all files */ int isJournal; /* True if this is a journal file */ - int nJrnl; /* Space allocated for aJrnl[] */ + unsigned int nJrnl; /* Space allocated for aJrnl[] */ char *aJrnl; /* Journal content */ + int szPage; /* Last known page size */ + sqlite3_int64 szDb; /* Database file size. -1 means unknown */ }; /* @@ -291,7 +298,8 @@ static int kvvfsEncode(const char *aData, int nData, char *aOut){ }else{ /* A sequence of 1 or more zeros is stored as a little-endian ** base-26 number using a..z as the digits. So one zero is "b". - ** Two zeros is "c". 25 zeros is "z", 26 zeros is "ba" and so forth. + ** Two zeros is "c". 25 zeros is "z", 26 zeros is "ab", 27 is "bb", + ** and so forth. */ int k; for(k=1; a[i+k]==0 && i+k='a' ){ int n = 0; + int mult = 1; while( c>='a' && c<='z' ){ - n = n*26 + c - 'a'; + n += (c - 'a')*mult; + mult *= 26; c = aIn[++i]; } if( j+n>nOut ) return -1; @@ -345,6 +357,44 @@ static int kvvfsDecode(const char *aIn, char *aOut, int nOut){ return j; } +/* +** Decode a complete journal file. Allocate space in pFile->aJrnl +** and store the decoding there. Or leave pFile->aJrnl set to NULL +** if an error is encountered. +** +** The first few characters of the text encoding will be a +** base-26 number (digits a..z) that is the total number of bytes +** in the decoding. Use this number to size the initial allocation. +*/ +static void kvvfsDecodeJournal( + KVVfsFile *pFile, /* Store decoding in pFile->aJrnl */ + const char *zTxt, /* Text encoding. Zero-terminated */ + int nTxt /* Bytes in zTxt, excluding zero terminator */ +){ + unsigned int n; + int c, i, mult; + i = 0; + mult = 1; + while( (c = zTxt[i])>='a' && c<='z' ){ + n += (zTxt[i] - 'a')*mult; + mult *= 26; + i++; + } + sqlite3_free(pFile->aJrnl); + pFile->aJrnl = sqlite3_malloc64( n ); + if( pFile->aJrnl==0 ){ + pFile->nJrnl = 0; + return; + } + pFile->nJrnl = n; + n = kvvfsDecode(zTxt+i, pFile->aJrnl, pFile->nJrnl); + if( nnJrnl ){ + sqlite3_free(pFile->aJrnl); + pFile->aJrnl = 0; + pFile->nJrnl = 0; + } +} + /* ** Read from the -journal file. */ @@ -384,7 +434,36 @@ static int kvvfsReadFromDb( int iAmt, sqlite_int64 iOfst ){ - return SQLITE_IOERR; + unsigned int pgno; + KVVfsPage *pPage; + int got; + char zKey[30]; + char aData[131073]; + assert( pFile->szDb>=0 ); + assert( iOfst>=0 ); + assert( iAmt>=0 ); + if( (iOfst % iAmt)!=0 ){ + return SQLITE_IOERR_READ; + } + if( iAmt!=100 || iOfst!=0 ){ + if( (iAmt & (iAmt-1))!=0 || iAmt<512 || iAmt>65536 ){ + return SQLITE_IOERR_READ; + } + pFile->szPage = iAmt; + } + pgno = 1 + iOfst/iAmt; + sqlite3_snprintf(sizeof(zKey), zKey, "pg-%u", pgno) + got = kvstorageRead(pFile->pVfs->pStore, zKey, aData, sizeof(aData)-1); + if( got<0 ){ + return SQLITE_IOERR_READ; + } + aData[got] = 0; + n = kvvfsDecode(aData, zBuf, iAmt); + if( npVfs->pStore, "sz", zData, sizeof(zData)-1); + return strtoll(zData, 0, 0); +} +static void kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){ + char zData[50]; + sqlite3_snprintf(sizeof(zData), zData, "%lld", sz); + kvstorageWrite(pFile->pVfs->pStore, "sz", zData); +} + /* ** Write into the -journal file. */ @@ -416,11 +510,25 @@ static int kvvfsWriteToJournal( int iAmt, sqlite_int64 iOfst ){ - return SQLITE_IOERR; + sqlite3_int64 iEnd = iOfst+iAmt; + if( iEnd>=0x10000000 ) return SQLITE_FULL; + if( pFile->aJrnl==0 || pFile->nJrnlaJrnl, iEnd); + if( aNew==0 ){ + return SQLITE_IOERR_NOMEM; + } + pFile->aJrnl = aNew; + if( pFile->nJrnlaJrnl+pFile->nJrnl, 0, iOfst-pFile->nJrnl); + } + pFile->nJrnl = iEnd; + } + memcpy(pFile->aJrnl+iOfst, zBuf, iAmt); + return SQLITE_OK; } /* -** Read from the database file. +** Write into the database file. */ static int kvvfsWriteToDb( KVVfsFile *pFile, @@ -428,7 +536,19 @@ static int kvvfsWriteToDb( int iAmt, sqlite_int64 iOfst ){ - return SQLITE_IOERR; + unsigned int pgno; + char zKey[30]; + char aData[131073]; + assert( iAmt>=512 && iAmt<=65536 ); + assert( (iAmt & (iAmt-1))==0 ); + pgno = 1 + iOfst/iAmt; + sqlite3_snprintf(sizeof(zKey), zKey, "pg-%u", pgno) + nData = kvvfsEncode(zBuf, iAmt, aData); + kvstorageWrite(pFile->pVfs->pStore, zKey, aData); + if( iOfst+iAmt > pFile->szDb ){ + pFile->szDb = iOfst + iAmt; + } + return SQLITE_OK; } @@ -454,20 +574,65 @@ static int kvvfsWrite( /* ** Truncate an kvvfs-file. */ -static int kvvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){ - int rc; +static int kvvfsTruncate(sqlite3_file *pProtoFile, sqlite_int64 size){ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; - rc = SQLITE_IOERR; - return rc; + if( pFile->isJournal ){ + assert( size==0 ); + kvstorageDelete(pFile->pVfs->pStore, "journal"); + sqlite3_free(pFile->aData); + pFile->aData = 0; + pFile->nData = 0; + return SQLITE_OK; + } + if( pFile->szDb>size + && pFile->szPage>0 + && (size % pFile->szPage)==0 + ){ + char zKey[50]; + unsigned int pgno, pgnoMax; + pgno = 1 + size/pFile->szPage; + pgnoMax = 2 + pFile->szDb/pFile->szPage; + while( pgno<=pgnoMax ){ + sqlite3_snprintf(sizeof(zKey), zKey, "pg-%u", pgno); + kvstorageDelete(pFile->pVfs->pStore, zKey); + pgno++; + } + pFile->szDb = size; + kvvfsWriteFileSize(pFile->pVfs->pStore, size); + return SQLITE_OK; + } + return SQLITE_IOERR; } /* ** Sync an kvvfs-file. */ -static int kvvfsSync(sqlite3_file *pFile, int flags){ - int rc; +static int kvvfsSync(sqlite3_file *pProtoFile, int flags){ + int i, k, n; KVVfsFile *pFile = (KVVfsFile *)pProtoFile; - rc = SQLITE_IOERR; + char *zOut; + if( !pFile->isJournal ){ + if( pFile->szDb>0 ){ + kvvfsWriteFileSize(pFile, pFile->szDb); + } + return SQLITE_OK; + } + if( pFile->nJrnl<=0 ){ + return kvvfsTruncate(pProtoFile, 0); + } + zOut = sqlite3_malloc64( pFile->nJrnl*2 + 50 ); + if( zOut==0 ){ + return SQLITE_IOERR_NOMEM; + } + n = pFile->nJrnl; + i = 0; + do{ + zOut[i++] = 'a' + (n%26); + n /= 26; + }while( n>0 ); + kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]); + kvstorageWrite(pFile->pVfs->pStore, "journal", zOut); + sqlite3_free(zOut); return rc; } @@ -475,31 +640,37 @@ static int kvvfsSync(sqlite3_file *pFile, int flags){ ** Return the current file-size of an kvvfs-file. */ static int kvvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ - int rc; KVVfsFile *pFile = (KVVfsFile *)pProtoFile; - *pSize = 0; - rc = SQLITE_IOERR; - return rc; + if( pFile->isJournal ){ + *pSize = pFile->nJrnl; + }else{ + *pSize = pFile->szDb; + } + return SQLITE_OK; } /* ** Lock an kvvfs-file. */ static int kvvfsLock(sqlite3_file *pFile, int eLock){ - int rc; KVVfsFile *pFile = (KVVfsFile *)pProtoFile; - rc = SQLITE_IOERR; - return rc; + assert( !pFile->isJournal ); + if( eLock!=SQLITE_LOCK_NONE ){ + pFile->szDb = kvvfsReadFileSize(pFile); + } + return SQLITE_OK; } /* ** Unlock an kvvfs-file. */ static int kvvfsUnlock(sqlite3_file *pFile, int eLock){ - int rc; KVVfsFile *pFile = (KVVfsFile *)pProtoFile; - rc = SQLITE_IOERR; - return rc; + assert( !pFile->isJournal ); + if( eLock==SQLITE_LOCK_NONE ){ + pFile->szDb = -1; + } + return SQLITE_OK; } /* @@ -509,7 +680,7 @@ static int kvvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){ int rc; KVVfsFile *pFile = (KVVfsFile *)pProtoFile; *pResOut = 0; - rc = SQLITE_IOERR; + rc = SQLITE_OK; return rc; } @@ -527,7 +698,7 @@ static int kvvfsFileControl(sqlite3_file *pFile, int op, void *pArg){ ** Return the sector-size in bytes for an kvvfs-file. */ static int kvvfsSectorSize(sqlite3_file *pFile){ - return 4096; + return 512; } /* @@ -548,12 +719,15 @@ static int kvvfsOpen( int flags, int *pOutFlags ){ - int rc; KVVfsFile *pFile = (KVVfsFile*)pProtoFile; KVVfsVfs *pVfs = (KVVfsVfs*)pProtoVfs; - + pFile->aJrnl = 0; + pFile->nJrnl = 0; + pFile->isJournal = sqlite3_strglob("*-journal", zName)==0; + pFile->szPage = -1; + pFile->szDb = -1; - return rc; + return SQLITE_OK; } /* @@ -574,12 +748,17 @@ static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ ** is available, or false otherwise. */ static int kvvfsAccess( - sqlite3_vfs *pVfs, + sqlite3_vfs *pProtoVfs, const char *zPath, int flags, int *pResOut ){ - *pResOut = 1; + KVVfsVfs *pVfs = (KVVfsVfs*)pProtoVfs; + if( sqlite3_strglob("*-journal", zPath)==0 ){ + *pResOut = kvstorageRead(pVfs->pStore, "journal", 0, 0)>0; + }else{ + *pResOut = 1; + } return SQLITE_OK; } diff --git a/manifest b/manifest index dfddb1fb37..f872ac3349 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Non-working\scode\stowards\sa\sVFS\sfor\stext\skey/value\sstorage. -D 2022-09-07T17:29:22.881 +C Code\ssnapshot.\s\sCompletely\suntested.\s\sProbably\sdoes\snot\scompile. +D 2022-09-08T15:48:01.413 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -335,7 +335,7 @@ F ext/misc/uint.c 053fed3bce2e89583afcd4bf804d75d659879bbcedac74d0fa9ed548839a03 F ext/misc/unionvtab.c 36237f0607ca954ac13a4a0e2d2ac40c33bc6e032a5f55f431713061ef1625f9 F ext/misc/urifuncs.c f71360d14fa9e7626b563f1f781c6148109462741c5235ac63ae0f8917b9c751 F ext/misc/uuid.c 5bb2264c1b64d163efa46509544fd7500cb8769cb7c16dd52052da8d961505cf -F ext/misc/vfskv.c aebaf8b59b70a066c2c0ba9344db0494ff50452ff6b4e79c9133e7a2ea112a99 +F ext/misc/vfskv.c cb82423dcc37af07953d71f15518e8a75d81b2dbab45dc237e181c5b03373703 F ext/misc/vfslog.c 3932ab932eeb2601dbc4447cb14d445aaa9fbe43b863ef5f014401c3420afd20 F ext/misc/vfsstat.c 474d08efc697b8eba300082cb1eb74a5f0f3df31ed257db1cb07e72ab0e53dfb F ext/misc/vtablog.c 5538acd0c8ddaae372331bee11608d76973436b77d6a91e8635cfc9432fba5ae @@ -2001,11 +2001,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 6b00ecb59fd303f7985902c35a46db9e729201d4beaedea46596b728d9e4b1c8 -R 26f26a2d9ce78c288f974e48234fd3a7 -T *branch * kv-vfs -T *sym-kv-vfs * -T -sym-trunk * +P f9c89ee8d5ef46342bea78fa8d4da058d9ea628183ec985642bbda1cdfeacd5f +R fed5a09fb1667ca82dde0796f5fd6c9d U drh -Z 68d678ebfc334c4be8e66c0fb69610ed +Z ee96129288f9f67591a72cc787991652 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d9d59da7ca..8e900ee9c5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f9c89ee8d5ef46342bea78fa8d4da058d9ea628183ec985642bbda1cdfeacd5f \ No newline at end of file +51e267696d25f2f83b5bb847d5254ab15573ef1a16ea3d7dd8279c73c0bee539 \ No newline at end of file From e3485ee9966a1d888cda478f92b098d13ab138dd Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 8 Sep 2022 16:27:50 +0000 Subject: [PATCH 062/428] Compiles without error, but untested. FossilOrigin-Name: a329939c32e33d8d00066ba3d4327f07cca1b4e67de0b8d85f1e7f98afe40954 --- ext/misc/vfskv.c | 130 ++++++++++++++++++++++++++++------------------- manifest | 12 ++--- manifest.uuid | 2 +- 3 files changed, 86 insertions(+), 58 deletions(-) diff --git a/ext/misc/vfskv.c b/ext/misc/vfskv.c index 0773f70945..5d71816a82 100644 --- a/ext/misc/vfskv.c +++ b/ext/misc/vfskv.c @@ -14,14 +14,25 @@ ** Key/Value storage engine where both keys and values must be pure ** text. */ -#include "sqlite3.h" +#include +SQLITE_EXTENSION_INIT1 #include #include #include -#include +#include #include #include #include +#include + +/***************************************************************************** +** Debugging logic +*/ +#if 1 +#define KVVFS_TRACE(X) printf X; +#else +#define KVVFS_TRACE(X) +#endif /***************************************************************************** @@ -37,7 +48,6 @@ static KVStorage *kvstorageOpen(void); static void kvstorageClose(KVStorage*); static int kvstorageWrite(KVStorage*, const char *zKey, const char *zData); static int kvstorageDelete(KVStorage*, const char *zKey); -static int kvstorageSize(KVStorage*, const char *zKey); static int kvstorageRead(KVStorage*, const char *zKey, char *zBuf, int nBuf); @@ -93,13 +103,9 @@ static int kvvfsDelete(sqlite3_vfs*, const char *zName, int syncDir); static int kvvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *); static int kvvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); static void *kvvfsDlOpen(sqlite3_vfs*, const char *zFilename); -static void kvvfsDlError(sqlite3_vfs*, int nByte, char *zErrMsg); -static void (*kvvfsDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void); -static void kvvfsDlClose(sqlite3_vfs*, void*); static int kvvfsRandomness(sqlite3_vfs*, int nByte, char *zOut); static int kvvfsSleep(sqlite3_vfs*, int microseconds); static int kvvfsCurrentTime(sqlite3_vfs*, double*); -static int kvvfsGetLastError(sqlite3_vfs*, int, char *); static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); static KVVfsVfs kvvfs_vfs = { @@ -122,7 +128,7 @@ static KVVfsVfs kvvfs_vfs = { kvvfsSleep, /* xSleep */ kvvfsCurrentTime, /* xCurrentTime */ 0, /* xGetLastError */ - kvvfsCurrentTimeInt64, /* xCurrentTimeInt64 */ + kvvfsCurrentTimeInt64 /* xCurrentTimeInt64 */ }, 0, 0 @@ -141,7 +147,7 @@ static sqlite3_io_methods kvvfs_io_methods = { kvvfsCheckReservedLock, /* xCheckReservedLock */ kvvfsFileControl, /* xFileControl */ kvvfsSectorSize, /* xSectorSize */ - kvvfsDeviceCharacteristics /* xDeviceCharacteristics */ + kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ 0, /* xShmMap */ 0, /* xShmLock */ 0, /* xShmBarrier */ @@ -188,12 +194,15 @@ static int kvstorageWrite( FILE *fd; kvstorageMakeKey(pStore, zKey); fd = fopen(pStore->zKey, "wb"); - if( fd==0 ) return 1; if( fd ){ + KVVFS_TRACE(("KVVFS-WRITE %-12s (%d) %.30s\n", pStore->zKey, + (int)strlen(zData), zData)); fputs(zData, fd); fclose(fd); + return 0; + }else{ + return 1; } - return 0; } /* Delete a key @@ -201,6 +210,7 @@ static int kvstorageWrite( static int kvstorageDelete(KVStorage *pStore, const char *zKey){ kvstorageMakeKey(pStore, zKey); unlink(pStore->zKey); + KVVFS_TRACE(("KVVFS-DELETE %-12s\n", pStore->zKey)); return 0; } @@ -229,22 +239,31 @@ static int kvstorageRead( || stat(pStore->zKey, &buf)!=0 || !S_ISREG(buf.st_mode) ){ + KVVFS_TRACE(("KVVFS-READ %-12s (-1)\n", pStore->zKey)); return -1; } if( nBuf<0 ){ return (int)buf.st_size; }else if( nBuf==1 ){ zBuf[0] = 0; + KVVFS_TRACE(("KVVFS-READ %-12s (%d)\n", pStore->zKey, + (int)buf.st_size)); return (int)buf.st_size; } if( nBuf-1 > buf.st_size ){ nBuf = buf.st_size + 1; } fd = fopen(pStore->zKey, "rb"); - if( fd==0 ) return -1; - fread(zBuf, nBuf-1, 1, fd); - fclose(fd); - return nBuf-1; + if( fd==0 ){ + KVVFS_TRACE(("KVVFS-READ %-12s (-1)\n", pStore->zKey)); + return -1; + }else{ + fread(zBuf, nBuf-1, 1, fd); + fclose(fd); + KVVFS_TRACE(("KVVFS-READ %-12s (%d) %.30s\n", pStore->zKey, + nBuf-1, zBuf)); + return nBuf-1; + } } @@ -274,7 +293,7 @@ static int kvvfsClose(sqlite3_file *pProtoFile){ pX = pX->pNext; } } - sqlite3_free(pFile->aData); + sqlite3_free(pFile->aJrnl); sqlite3_free(pFile); return SQLITE_OK; } @@ -289,7 +308,7 @@ static int kvvfsClose(sqlite3_file *pProtoFile){ */ static int kvvfsEncode(const char *aData, int nData, char *aOut){ int i, j; - const unsigned *a = (const unsigned char*)aData; + const unsigned char *a = (const unsigned char*)aData; for(i=j=0; ipVfs->pStore, "journal", aTxt, szTxt+1); kvvfsDecodeJournal(pFile, aTxt, szTxt); sqlite3_free(aTxt); - if( pFile->aData==0 ) return SQLITE_IOERR; + if( pFile->aJrnl==0 ) return SQLITE_IOERR; } if( iOfst+iAmt>pFile->nJrnl ){ return SQLITE_IOERR_SHORT_READ; } - mcmcpy(zBuf, pFile->aJrnl+iOfst, iAmt); + memcpy(zBuf, pFile->aJrnl+iOfst, iAmt); return SQLITE_OK; } @@ -435,8 +453,7 @@ static int kvvfsReadFromDb( sqlite_int64 iOfst ){ unsigned int pgno; - KVVfsPage *pPage; - int got; + int got, n; char zKey[30]; char aData[131073]; assert( pFile->szDb>=0 ); @@ -452,7 +469,7 @@ static int kvvfsReadFromDb( pFile->szPage = iAmt; } pgno = 1 + iOfst/iAmt; - sqlite3_snprintf(sizeof(zKey), zKey, "pg-%u", pgno) + sqlite3_snprintf(sizeof(zKey), zKey, "pg-%u", pgno); got = kvstorageRead(pFile->pVfs->pStore, zKey, aData, sizeof(aData)-1); if( got<0 ){ return SQLITE_IOERR_READ; @@ -542,8 +559,8 @@ static int kvvfsWriteToDb( assert( iAmt>=512 && iAmt<=65536 ); assert( (iAmt & (iAmt-1))==0 ); pgno = 1 + iOfst/iAmt; - sqlite3_snprintf(sizeof(zKey), zKey, "pg-%u", pgno) - nData = kvvfsEncode(zBuf, iAmt, aData); + sqlite3_snprintf(sizeof(zKey), zKey, "pg-%u", pgno); + kvvfsEncode(zBuf, iAmt, aData); kvstorageWrite(pFile->pVfs->pStore, zKey, aData); if( iOfst+iAmt > pFile->szDb ){ pFile->szDb = iOfst + iAmt; @@ -579,9 +596,9 @@ static int kvvfsTruncate(sqlite3_file *pProtoFile, sqlite_int64 size){ if( pFile->isJournal ){ assert( size==0 ); kvstorageDelete(pFile->pVfs->pStore, "journal"); - sqlite3_free(pFile->aData); - pFile->aData = 0; - pFile->nData = 0; + sqlite3_free(pFile->aJrnl); + pFile->aJrnl = 0; + pFile->nJrnl = 0; return SQLITE_OK; } if( pFile->szDb>size @@ -598,7 +615,7 @@ static int kvvfsTruncate(sqlite3_file *pProtoFile, sqlite_int64 size){ pgno++; } pFile->szDb = size; - kvvfsWriteFileSize(pFile->pVfs->pStore, size); + kvvfsWriteFileSize(pFile, size); return SQLITE_OK; } return SQLITE_IOERR; @@ -608,7 +625,7 @@ static int kvvfsTruncate(sqlite3_file *pProtoFile, sqlite_int64 size){ ** Sync an kvvfs-file. */ static int kvvfsSync(sqlite3_file *pProtoFile, int flags){ - int i, k, n; + int i, n; KVVfsFile *pFile = (KVVfsFile *)pProtoFile; char *zOut; if( !pFile->isJournal ){ @@ -633,13 +650,13 @@ static int kvvfsSync(sqlite3_file *pProtoFile, int flags){ kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]); kvstorageWrite(pFile->pVfs->pStore, "journal", zOut); sqlite3_free(zOut); - return rc; + return SQLITE_OK; } /* ** Return the current file-size of an kvvfs-file. */ -static int kvvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ +static int kvvfsFileSize(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; if( pFile->isJournal ){ *pSize = pFile->nJrnl; @@ -652,7 +669,7 @@ static int kvvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ /* ** Lock an kvvfs-file. */ -static int kvvfsLock(sqlite3_file *pFile, int eLock){ +static int kvvfsLock(sqlite3_file *pProtoFile, int eLock){ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; assert( !pFile->isJournal ); if( eLock!=SQLITE_LOCK_NONE ){ @@ -664,7 +681,7 @@ static int kvvfsLock(sqlite3_file *pFile, int eLock){ /* ** Unlock an kvvfs-file. */ -static int kvvfsUnlock(sqlite3_file *pFile, int eLock){ +static int kvvfsUnlock(sqlite3_file *pProtoFile, int eLock){ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; assert( !pFile->isJournal ); if( eLock==SQLITE_LOCK_NONE ){ @@ -676,22 +693,16 @@ static int kvvfsUnlock(sqlite3_file *pFile, int eLock){ /* ** Check if another file-handle holds a RESERVED lock on an kvvfs-file. */ -static int kvvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){ - int rc; - KVVfsFile *pFile = (KVVfsFile *)pProtoFile; +static int kvvfsCheckReservedLock(sqlite3_file *pProtoFile, int *pResOut){ *pResOut = 0; - rc = SQLITE_OK; - return rc; + return SQLITE_OK; } /* ** File control method. For custom operations on an kvvfs-file. */ -static int kvvfsFileControl(sqlite3_file *pFile, int op, void *pArg){ - int rc; - KVVfsFile *pFile = (KVVfsFile *)pProtoFile; - rc = SQLITE_NOTFOUND; - return rc; +static int kvvfsFileControl(sqlite3_file *pProtoFile, int op, void *pArg){ + return SQLITE_NOTFOUND; } /* @@ -704,7 +715,7 @@ static int kvvfsSectorSize(sqlite3_file *pFile){ /* ** Return the device characteristic flags supported by an kvvfs-file. */ -static int kvvfsDeviceCharacteristics(sqlite3_file *pFile){ +static int kvvfsDeviceCharacteristics(sqlite3_file *pProtoFile){ return 0; } @@ -726,7 +737,13 @@ static int kvvfsOpen( pFile->isJournal = sqlite3_strglob("*-journal", zName)==0; pFile->szPage = -1; pFile->szDb = -1; - + pFile->base.pMethods = &kvvfs_io_methods; + pFile->pVfs = pVfs; + if( pVfs->pFiles==0 ){ + pVfs->pStore = kvstorageOpen(); + } + pFile->pNext = pVfs->pFiles; + pVfs->pFiles = pFile; return SQLITE_OK; } @@ -776,7 +793,7 @@ static int kvvfsFullPathname( size_t nPath = strlen(zPath); if( nOut Date: Thu, 8 Sep 2022 17:12:12 +0000 Subject: [PATCH 063/428] Compiles and loads as an extension. Starts to run but quickly hits issues. FossilOrigin-Name: 2e38726f46918b28b5c638967f960a20afd3fe84ad245f3bea39a1d64980435b --- ext/misc/vfskv.c | 23 ++++++++++++----------- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/ext/misc/vfskv.c b/ext/misc/vfskv.c index 5d71816a82..bde2f08336 100644 --- a/ext/misc/vfskv.c +++ b/ext/misc/vfskv.c @@ -195,7 +195,7 @@ static int kvstorageWrite( kvstorageMakeKey(pStore, zKey); fd = fopen(pStore->zKey, "wb"); if( fd ){ - KVVFS_TRACE(("KVVFS-WRITE %-12s (%d) %.30s\n", pStore->zKey, + KVVFS_TRACE(("KVVFS-WRITE %-14s (%d) %.30s\n", pStore->zKey, (int)strlen(zData), zData)); fputs(zData, fd); fclose(fd); @@ -210,7 +210,7 @@ static int kvstorageWrite( static int kvstorageDelete(KVStorage *pStore, const char *zKey){ kvstorageMakeKey(pStore, zKey); unlink(pStore->zKey); - KVVFS_TRACE(("KVVFS-DELETE %-12s\n", pStore->zKey)); + KVVFS_TRACE(("KVVFS-DELETE %-14s\n", pStore->zKey)); return 0; } @@ -239,14 +239,14 @@ static int kvstorageRead( || stat(pStore->zKey, &buf)!=0 || !S_ISREG(buf.st_mode) ){ - KVVFS_TRACE(("KVVFS-READ %-12s (-1)\n", pStore->zKey)); + KVVFS_TRACE(("KVVFS-READ %-14s (-1)\n", pStore->zKey)); return -1; } if( nBuf<0 ){ return (int)buf.st_size; }else if( nBuf==1 ){ zBuf[0] = 0; - KVVFS_TRACE(("KVVFS-READ %-12s (%d)\n", pStore->zKey, + KVVFS_TRACE(("KVVFS-READ %-14s (%d)\n", pStore->zKey, (int)buf.st_size)); return (int)buf.st_size; } @@ -255,12 +255,12 @@ static int kvstorageRead( } fd = fopen(pStore->zKey, "rb"); if( fd==0 ){ - KVVFS_TRACE(("KVVFS-READ %-12s (-1)\n", pStore->zKey)); + KVVFS_TRACE(("KVVFS-READ %-14s (-1)\n", pStore->zKey)); return -1; }else{ fread(zBuf, nBuf-1, 1, fd); fclose(fd); - KVVFS_TRACE(("KVVFS-READ %-12s (%d) %.30s\n", pStore->zKey, + KVVFS_TRACE(("KVVFS-READ %-14s (%d) %.30s\n", pStore->zKey, nBuf-1, zBuf)); return nBuf-1; } @@ -456,7 +456,6 @@ static int kvvfsReadFromDb( int got, n; char zKey[30]; char aData[131073]; - assert( pFile->szDb>=0 ); assert( iOfst>=0 ); assert( iAmt>=0 ); if( (iOfst % iAmt)!=0 ){ @@ -472,10 +471,11 @@ static int kvvfsReadFromDb( sqlite3_snprintf(sizeof(zKey), zKey, "pg-%u", pgno); got = kvstorageRead(pFile->pVfs->pStore, zKey, aData, sizeof(aData)-1); if( got<0 ){ - return SQLITE_IOERR_READ; + n = 0; + }else{ + aData[got] = 0; + n = kvvfsDecode(aData, zBuf, iAmt); } - aData[got] = 0; - n = kvvfsDecode(aData, zBuf, iAmt); if( n Date: Thu, 8 Sep 2022 17:42:33 +0000 Subject: [PATCH 064/428] Fix problems with recovering the sqlite_sequence table. FossilOrigin-Name: 356d2209ea5f6b69ce15b62027c63419c2d039e52f01c74a3810a6317abf4fb0 --- ext/recover/recover1.test | 26 ++++- ext/recover/sqlite3recover.c | 183 ++++++++++++++++++----------------- manifest | 14 +-- manifest.uuid | 2 +- 4 files changed, 128 insertions(+), 97 deletions(-) diff --git a/ext/recover/recover1.test b/ext/recover/recover1.test index 167b2796bd..26e7c60d26 100644 --- a/ext/recover/recover1.test +++ b/ext/recover/recover1.test @@ -45,6 +45,7 @@ proc do_recover_test {tn} { uplevel [list do_test $tn.1 { set R [sqlite3_recover_init db main test.db2] $R config testdb rstate.db + $R config rowids 1 $R step $R finish } {}] @@ -60,12 +61,14 @@ proc do_recover_test {tn} { set ::sqlhook [list] set R [sqlite3_recover_init_sql db main my_sql_hook] $R config testdb rstate.db + $R config rowids 1 $R step $R finish } {}] sqlite3 db2 test.db2 execsql [join $::sqlhook ";"] db2 + # puts [join $::sqlhook ";\n"] uplevel [list do_test $tn.4 [list compare_dbs db db2] {}] db2 close } @@ -82,6 +85,7 @@ do_execsql_test 1.0 { ) INSERT INTO t1 SELECT i*2, hex(randomblob(250)) FROM s; INSERT INTO t2 SELECT * FROM t1; + } do_recover_test 1 @@ -131,8 +135,28 @@ do_execsql_test 7.1 { SELECT * FROM t2 } {10 11 ten} -breakpoint do_recover_test 7.2 +#-------------------------------------------------------------------------- +# +reset_db +do_execsql_test 8.0 { + CREATE TABLE x1(a INTEGER PRIMARY KEY AUTOINCREMENT, b, c); + WITH s(i) AS ( + SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<2 + ) + INSERT INTO x1(b, c) SELECT hex(randomblob(100)), hex(randomblob(100)) FROM s; + + CREATE INDEX x1b ON x1(b); + CREATE INDEX x1cb ON x1(c, b); + DELETE FROM x1 WHERE a>50; + + ANALYZE; +} + +do_recover_test 8 + + + finish_test diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index de3dc6ea1a..1ab4f5d6d5 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -60,17 +60,6 @@ struct RecoverBitmap { u32 aElem[0]; /* Array of 32-bit bitmasks */ }; -/* -** -*/ -#define RECOVERY_SCHEMA \ -" CREATE TABLE recovery.map(" \ -" pgno INTEGER PRIMARY KEY, parent INT" \ -" );" \ -" CREATE TABLE recovery.schema(" \ -" type, name, tbl_name, rootpage, sql" \ -" );" - struct sqlite3_recover { sqlite3 *dbIn; @@ -1123,6 +1112,8 @@ static int recoverWriteData(sqlite3_recover *p){ RecoverTable *pTbl; int nMax = 0; sqlite3_value **apVal = 0; + + sqlite3_stmt *pTbls = 0; sqlite3_stmt *pSel = 0; /* Figure out the maximum number of columns for any table in the schema */ @@ -1133,114 +1124,130 @@ static int recoverWriteData(sqlite3_recover *p){ apVal = (sqlite3_value**)recoverMalloc(p, sizeof(sqlite3_value*) * (nMax+1)); if( apVal==0 ) return p->errCode; + pTbls = recoverPrepare(p, p->dbOut, + "SELECT rootpage FROM recovery.schema WHERE type='table'" + " ORDER BY (tbl_name='sqlite_sequence') ASC" + ); + pSel = recoverPrepare(p, p->dbOut, - "WITH RECURSIVE pages(root, page) AS (" - " SELECT rootpage, rootpage FROM recovery.schema" + "WITH RECURSIVE pages(page) AS (" + " SELECT ?1" " UNION" - " SELECT root, child FROM sqlite_dbptr('getpage()'), pages " + " SELECT child FROM sqlite_dbptr('getpage()'), pages " " WHERE pgno=page" ") " - "SELECT root, page, cell, field, value " + "SELECT page, cell, field, value " "FROM sqlite_dbdata('getpage()') d, pages p WHERE p.page=d.pgno " "UNION ALL " - "SELECT 0, 0, 0, 0, 0" + "SELECT 0, 0, 0, 0" ); if( pSel ){ - RecoverTable *pTab = 0; - sqlite3_stmt *pInsert = 0; - int nInsert = -1; - i64 iPrevRoot = -1; - i64 iPrevPage = -1; - int iPrevCell = -1; - int bHaveRowid = 0; /* True if iRowid is valid */ - i64 iRowid = 0; - int nVal = -1; - while( p->errCode==SQLITE_OK && sqlite3_step(pSel)==SQLITE_ROW ){ - i64 iRoot = sqlite3_column_int64(pSel, 0); - i64 iPage = sqlite3_column_int64(pSel, 1); - int iCell = sqlite3_column_int(pSel, 2); - int iField = sqlite3_column_int(pSel, 3); - sqlite3_value *pVal = sqlite3_column_value(pSel, 4); + /* The outer loop runs once for each table to recover. */ + while( sqlite3_step(pTbls)==SQLITE_ROW ){ + i64 iRoot = sqlite3_column_int64(pTbls, 0); + RecoverTable *pTab = recoverFindTable(p, iRoot); + if( pTab ){ + int ii; + sqlite3_stmt *pInsert = 0; + int nInsert = -1; + i64 iPrevPage = -1; + int iPrevCell = -1; + int bHaveRowid = 0; /* True if iRowid is valid */ + i64 iRowid = 0; + int nVal = -1; - int bNewCell = (iPrevRoot!=iRoot || iPrevPage!=iPage || iPrevCell!=iCell); - assert( bNewCell==0 || (iField==-1 || iField==0) ); - assert( bNewCell || iField==nVal ); + if( sqlite3_stricmp("sqlite_sequence", pTab->zTab)==0 ){ + recoverExec(p, p->dbOut, "DELETE FROM sqlite_sequence"); + recoverSqlCallback(p, "DELETE FROM sqlite_sequence"); + } - if( bNewCell ){ - if( nVal>=0 ){ - int ii; + sqlite3_bind_int64(pSel, 1, iRoot); + while( p->errCode==SQLITE_OK && sqlite3_step(pSel)==SQLITE_ROW ){ + i64 iPage = sqlite3_column_int64(pSel, 0); + int iCell = sqlite3_column_int(pSel, 1); + int iField = sqlite3_column_int(pSel, 2); + sqlite3_value *pVal = sqlite3_column_value(pSel, 3); - if( pTab ){ - int iVal = 0; - int iBind = 1; + int bNewCell = (iPrevPage!=iPage || iPrevCell!=iCell); + assert( bNewCell==0 || (iField==-1 || iField==0) ); + assert( bNewCell || iField==nVal ); - if( pInsert==0 || nVal!=nInsert ){ - recoverFinalize(p, pInsert); - pInsert = recoverInsertStmt(p, pTab, nVal); - nInsert = nVal; - } + if( bNewCell ){ + if( nVal>=0 ){ + int ii; + int iVal = 0; + int iBind = 1; - for(ii=0; iinCol; ii++){ - RecoverColumn *pCol = &pTab->aCol[ii]; + if( pInsert==0 || nVal!=nInsert ){ + recoverFinalize(p, pInsert); + pInsert = recoverInsertStmt(p, pTab, nVal); + nInsert = nVal; + } - if( pCol->iBind>0 ){ - if( pCol->bIPK ){ - sqlite3_bind_int64(pInsert, pCol->iBind, iRowid); - }else if( pCol->iFieldiBind, apVal[pCol->iField]); + for(ii=0; iinCol; ii++){ + RecoverColumn *pCol = &pTab->aCol[ii]; + + if( pCol->iBind>0 ){ + if( pCol->bIPK ){ + sqlite3_bind_int64(pInsert, pCol->iBind, iRowid); + }else if( pCol->iFieldiBind,apVal[pCol->iField]); + } } } - } - if( p->bRecoverRowid && pTab->iRowidBind>0 && bHaveRowid ){ - sqlite3_bind_int64(pInsert, pTab->iRowidBind, iRowid); + if( p->bRecoverRowid && pTab->iRowidBind>0 && bHaveRowid ){ + sqlite3_bind_int64(pInsert, pTab->iRowidBind, iRowid); + } + + if( SQLITE_ROW==sqlite3_step(pInsert) && p->xSql ){ + const char *zSql = (const char*)sqlite3_column_text(pInsert, 0); + recoverSqlCallback(p, zSql); + } + recoverReset(p, pInsert); + assert( p->errCode || pInsert ); + if( pInsert ) sqlite3_clear_bindings(pInsert); } - if( SQLITE_ROW==sqlite3_step(pInsert) && p->xSql ){ - const char *zSql = (const char*)sqlite3_column_text(pInsert, 0); - recoverSqlCallback(p, zSql); + for(ii=0; iierrCode || pInsert ); - if( pInsert ) sqlite3_clear_bindings(pInsert); + nVal = -1; + bHaveRowid = 0; } - for(ii=0; iierrCode; } diff --git a/manifest b/manifest index 421372fcab..f5344ad585 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\snew\stest\sfile\srecoverclobber.test. -D 2022-09-08T11:04:23.161 +C Fix\sproblems\swith\srecovering\sthe\ssqlite_sequence\stable. +D 2022-09-08T17:42:33.679 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -387,12 +387,12 @@ F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2 F ext/rbu/sqlite3rbu.c 8737cabdfbee84bb25a7851ecef8b1312be332761238da9be6ddb10c62ad4291 F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812 F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a -F ext/recover/recover1.test ae8ce9828210aa6c466bf88e23b0933849d5bb43091abe48cf2e56d636e51e93 +F ext/recover/recover1.test ddc322148170eafe1dabbea91ac175a72f7e7d2777619a6434696a310beff9a3 F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c F ext/recover/recoverclobber.test e6537ebf99f57bfff6cca59550b5f4278319b57a89865abb98d755a8fd561d84 F ext/recover/recoverold.test f368a6ae2db12b6017257b332a19ab5df527f4061e43f12f5c85d8e2b236f074 F ext/recover/recoverrowid.test ec4436cd69e6cdacb48dd2963ff6dd9dbd5fe648376de5e7c0c2f4f6cbacb417 -F ext/recover/sqlite3recover.c 9724f913fd457f655e2873552bc6600a6aaff7104b9113ccb38fea18b6a71f03 +F ext/recover/sqlite3recover.c 2c45ab8cce41dcb578ef739652e65675d161751fe0d979b806d947a02de7fd32 F ext/recover/sqlite3recover.h 32f89b66f0235c0d94dfee0f1c3e9ed1ad726b3c4f3716ef0262b31cc33e8301 F ext/recover/test_recover.c 7aa268d3431d630eaa82ce14974ae04be50fe7feba660ffaea009cd581916d27 F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 @@ -2007,8 +2007,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 6cca8913e703635ad89415a60fc84000ac188d9df43f45594b8ad87facb91d54 -R 102ea2ce4694a6be6ee34f8e72c4fbd9 +P cb4e950c472bd24a79a8505a7f8e4c3a0f7821648297d05cc760738b777d5149 +R e45081ba4805e1d831caf7a65d0c00b9 U dan -Z e86817dfd35f39cea8c450fbde6bc553 +Z 8c44bc5cd3a46bb8aa19fae7f571d64e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2f04809195..34b201c3c5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cb4e950c472bd24a79a8505a7f8e4c3a0f7821648297d05cc760738b777d5149 \ No newline at end of file +356d2209ea5f6b69ce15b62027c63419c2d039e52f01c74a3810a6317abf4fb0 \ No newline at end of file From 9a27d65044d64cf4ad49b618244f3de022a39d9e Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 8 Sep 2022 19:22:29 +0000 Subject: [PATCH 065/428] Update the shell to use the recover extension for the .recover command. FossilOrigin-Name: ae832e77084eddd696c80cb926d070a5db9d45dce34156a02522b3140e8f5e8b --- ext/recover/recover1.test | 2 - ext/recover/sqlite3recover.c | 16 +- ext/recover/sqlite3recover.h | 8 +- ext/recover/test_recover.c | 4 +- manifest | 20 +- manifest.uuid | 2 +- src/shell.c.in | 658 ++--------------------------------- 7 files changed, 54 insertions(+), 656 deletions(-) diff --git a/ext/recover/recover1.test b/ext/recover/recover1.test index 26e7c60d26..9afe43209f 100644 --- a/ext/recover/recover1.test +++ b/ext/recover/recover1.test @@ -45,7 +45,6 @@ proc do_recover_test {tn} { uplevel [list do_test $tn.1 { set R [sqlite3_recover_init db main test.db2] $R config testdb rstate.db - $R config rowids 1 $R step $R finish } {}] @@ -68,7 +67,6 @@ proc do_recover_test {tn} { sqlite3 db2 test.db2 execsql [join $::sqlhook ";"] db2 - # puts [join $::sqlhook ";\n"] uplevel [list do_test $tn.4 [list compare_dbs db db2] {}] db2 close } diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index 1ab4f5d6d5..cef2fb9da7 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -84,6 +84,11 @@ struct sqlite3_recover { int (*xSql)(void*,const char*); }; +/* +** Default value for SQLITE_RECOVER_ROWIDS (sqlite3_recover.bRecoverRowid). +*/ +#define RECOVER_ROWID_DEFAULT 1 + /* ** Like strlen(). But handles NULL pointer arguments. */ @@ -371,7 +376,7 @@ static void recoverGetPage( ** Try to use zA and zB first. If both of those are already found in z[] ** then make up some string and store it in the buffer zBuf. */ -static const char *unused_string( +static const char *recoverUnusedString( const char *z, /* Result must not appear anywhere in z */ const char *zA, const char *zB, /* Try these first */ char *zBuf /* Space to store a generated string */ @@ -416,11 +421,11 @@ static void recoverEscapeCrnl( for(i=0; zText[i]; i++){ if( zNL==0 && zText[i]=='\n' ){ - zNL = unused_string(zText, "\\n", "\\012", zBuf1); + zNL = recoverUnusedString(zText, "\\n", "\\012", zBuf1); nNL = (int)strlen(zNL); } if( zCR==0 && zText[i]=='\r' ){ - zCR = unused_string(zText, "\\r", "\\015", zBuf2); + zCR = recoverUnusedString(zText, "\\r", "\\015", zBuf2); nCR = (int)strlen(zCR); } } @@ -1281,6 +1286,7 @@ sqlite3_recover *recoverInit( memcpy(pRet->zUri, zUri, nUri); pRet->xSql = xSql; pRet->pSqlCtx = pSqlCtx; + pRet->bRecoverRowid = RECOVER_ROWID_DEFAULT; } return pRet; @@ -1330,11 +1336,11 @@ int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){ break; case SQLITE_RECOVER_FREELIST_CORRUPT: - p->bFreelistCorrupt = (pArg ? 1 : 0); + p->bFreelistCorrupt = *(int*)pArg; break; case SQLITE_RECOVER_ROWIDS: - p->bRecoverRowid = (pArg ? 1 : 0); + p->bRecoverRowid = *(int*)pArg; break; default: diff --git a/ext/recover/sqlite3recover.h b/ext/recover/sqlite3recover.h index 5dc2a56d30..1ef707e13c 100644 --- a/ext/recover/sqlite3recover.h +++ b/ext/recover/sqlite3recover.h @@ -16,7 +16,7 @@ #ifndef _SQLITE_RECOVER_H #define _SQLITE_RECOVER_H -#include "sqlite3.h" /* Required for error code definitions */ +#include "sqlite3.h" #ifdef __cplusplus extern "C" { @@ -58,9 +58,9 @@ int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg); ** pages to add to the lost-and-found table. ** ** SQLITE_RECOVER_FREELIST_CORRUPT: -** The pArg value must actually be integer (type "int") value 0 or 1 -** cast as a (void*). If this option is set (argument is 1) and -** a lost-and-found table has been configured using +** The pArg value must actually be a pointer to a value of type +** int containing value 0 or 1 cast as a (void*). If this option is set +** (argument is 1) and a lost-and-found table has been configured using ** SQLITE_RECOVER_LOST_AND_FOUND, then is assumed that the freelist is ** corrupt and an attempt is made to recover records from pages that ** appear to be linked into the freelist. Otherwise, pages on the freelist diff --git a/ext/recover/test_recover.c b/ext/recover/test_recover.c index 1de99f4bd6..83166e994c 100644 --- a/ext/recover/test_recover.c +++ b/ext/recover/test_recover.c @@ -129,7 +129,7 @@ static int testRecoverCmd( int iVal = 0; if( Tcl_GetBooleanFromObj(interp, objv[3], &iVal) ) return TCL_ERROR; res = sqlite3_recover_config(pTest->p, - SQLITE_RECOVER_FREELIST_CORRUPT, SQLITE_INT_TO_PTR(iVal) + SQLITE_RECOVER_FREELIST_CORRUPT, (void*)&iVal ); break; } @@ -137,7 +137,7 @@ static int testRecoverCmd( int iVal = 0; if( Tcl_GetBooleanFromObj(interp, objv[3], &iVal) ) return TCL_ERROR; res = sqlite3_recover_config(pTest->p, - SQLITE_RECOVER_ROWIDS, SQLITE_INT_TO_PTR(iVal) + SQLITE_RECOVER_ROWIDS, (void*)&iVal ); break; } diff --git a/manifest b/manifest index f5344ad585..013ce9cd0f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sproblems\swith\srecovering\sthe\ssqlite_sequence\stable. -D 2022-09-08T17:42:33.679 +C Update\sthe\sshell\sto\suse\sthe\srecover\sextension\sfor\sthe\s.recover\scommand. +D 2022-09-08T19:22:29.395 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -387,14 +387,14 @@ F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2 F ext/rbu/sqlite3rbu.c 8737cabdfbee84bb25a7851ecef8b1312be332761238da9be6ddb10c62ad4291 F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812 F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a -F ext/recover/recover1.test ddc322148170eafe1dabbea91ac175a72f7e7d2777619a6434696a310beff9a3 +F ext/recover/recover1.test aa9f7f46b7209cae6d52321052d4440bc8f82b93991e693c4bad20a6f05a53e5 F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c F ext/recover/recoverclobber.test e6537ebf99f57bfff6cca59550b5f4278319b57a89865abb98d755a8fd561d84 F ext/recover/recoverold.test f368a6ae2db12b6017257b332a19ab5df527f4061e43f12f5c85d8e2b236f074 F ext/recover/recoverrowid.test ec4436cd69e6cdacb48dd2963ff6dd9dbd5fe648376de5e7c0c2f4f6cbacb417 -F ext/recover/sqlite3recover.c 2c45ab8cce41dcb578ef739652e65675d161751fe0d979b806d947a02de7fd32 -F ext/recover/sqlite3recover.h 32f89b66f0235c0d94dfee0f1c3e9ed1ad726b3c4f3716ef0262b31cc33e8301 -F ext/recover/test_recover.c 7aa268d3431d630eaa82ce14974ae04be50fe7feba660ffaea009cd581916d27 +F ext/recover/sqlite3recover.c b44241ca3b0cca2b50cfa7f715f79f4eddbe87d56e3baadd4e7bafe4c5872550 +F ext/recover/sqlite3recover.h 218cd9ba8c5c66c3841ca5014910982dc956cba5274257a0ecefb889db879133 +F ext/recover/test_recover.c 68b095ad396d8b1d9242ea663a4be1ad7585a46b1fc03483e9a692c8a87d2674 F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 F ext/repair/checkfreelist.c e21f06995ff4efdc1622dcceaea4dcba2caa83ca2f31a1607b98a8509168a996 F ext/repair/checkindex.c 4383e4469c21e5b9ae321d0d63cec53e981af9d7a6564be6374f0eeb93dfc890 @@ -597,7 +597,7 @@ F src/random.c 546d6feb15ec69c1aafe9bb351a277cbb498fd5410e646add673acb805714960 F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c ccce37e7fbe71089cf6aec91e7134c9c0c1d4840cff9f02587bbc71240d914a5 -F src/shell.c.in e7e7c2c69ae86c5ee9e8ad66227203d46ff6dce8700a1b1dababff01c71d33df +F src/shell.c.in b36581c005ebaa59b1eeb143bd3ad5a4b273bf15380ccac63ac8cdf4f0c4d3c9 F src/sqlite.h.in b9b7fd73239d94db20332bb6e504688001e5564b655e1318a4427a1caef4b99e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h a988810c9b21c0dc36dc7a62735012339dc76fc7ab448fb0792721d30eacb69d @@ -2007,8 +2007,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P cb4e950c472bd24a79a8505a7f8e4c3a0f7821648297d05cc760738b777d5149 -R e45081ba4805e1d831caf7a65d0c00b9 +P 356d2209ea5f6b69ce15b62027c63419c2d039e52f01c74a3810a6317abf4fb0 +R 9479c02b6838b39b62d6729211ce276e U dan -Z 8c44bc5cd3a46bb8aa19fae7f571d64e +Z df9c400431a19b9f6f7750b759f567b5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 34b201c3c5..8cbff6b606 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -356d2209ea5f6b69ce15b62027c63419c2d039e52f01c74a3810a6317abf4fb0 \ No newline at end of file +ae832e77084eddd696c80cb926d070a5db9d45dce34156a02522b3140e8f5e8b \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index d6f3a0aeb6..2a2aa81463 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1039,6 +1039,8 @@ INCLUDE ../ext/expert/sqlite3expert.c #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) INCLUDE ../ext/misc/dbdata.c +INCLUDE ../ext/recover/sqlite3recover.h +INCLUDE ../ext/recover/sqlite3recover.c #endif #if defined(SQLITE_ENABLE_SESSION) @@ -7252,363 +7254,15 @@ end_ar_command: #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) */ #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) -/* -** If (*pRc) is not SQLITE_OK when this function is called, it is a no-op. -** Otherwise, the SQL statement or statements in zSql are executed using -** database connection db and the error code written to *pRc before -** this function returns. -*/ -static void shellExec(sqlite3 *db, int *pRc, const char *zSql){ - int rc = *pRc; - if( rc==SQLITE_OK ){ - char *zErr = 0; - rc = sqlite3_exec(db, zSql, 0, 0, &zErr); - if( rc!=SQLITE_OK ){ - raw_printf(stderr, "SQL error: %s\n", zErr); - } - sqlite3_free(zErr); - *pRc = rc; - } -} /* -** Like shellExec(), except that zFmt is a printf() style format string. +** This function is used as a callback by the recover extension. Simply +** print the supplied SQL statement to stdout. */ -static void shellExecPrintf(sqlite3 *db, int *pRc, const char *zFmt, ...){ - char *z = 0; - if( *pRc==SQLITE_OK ){ - va_list ap; - va_start(ap, zFmt); - z = sqlite3_vmprintf(zFmt, ap); - va_end(ap); - if( z==0 ){ - *pRc = SQLITE_NOMEM; - }else{ - shellExec(db, pRc, z); - } - sqlite3_free(z); - } -} - -/* -** If *pRc is not SQLITE_OK when this function is called, it is a no-op. -** Otherwise, an attempt is made to allocate, zero and return a pointer -** to a buffer nByte bytes in size. If an OOM error occurs, *pRc is set -** to SQLITE_NOMEM and NULL returned. -*/ -static void *shellMalloc(int *pRc, sqlite3_int64 nByte){ - void *pRet = 0; - if( *pRc==SQLITE_OK ){ - pRet = sqlite3_malloc64(nByte); - if( pRet==0 ){ - *pRc = SQLITE_NOMEM; - }else{ - memset(pRet, 0, nByte); - } - } - return pRet; -} - -/* -** If *pRc is not SQLITE_OK when this function is called, it is a no-op. -** Otherwise, zFmt is treated as a printf() style string. The result of -** formatting it along with any trailing arguments is written into a -** buffer obtained from sqlite3_malloc(), and pointer to which is returned. -** It is the responsibility of the caller to eventually free this buffer -** using a call to sqlite3_free(). -** -** If an OOM error occurs, (*pRc) is set to SQLITE_NOMEM and a NULL -** pointer returned. -*/ -static char *shellMPrintf(int *pRc, const char *zFmt, ...){ - char *z = 0; - if( *pRc==SQLITE_OK ){ - va_list ap; - va_start(ap, zFmt); - z = sqlite3_vmprintf(zFmt, ap); - va_end(ap); - if( z==0 ){ - *pRc = SQLITE_NOMEM; - } - } - return z; -} - - -/* -** When running the ".recover" command, each output table, and the special -** orphaned row table if it is required, is represented by an instance -** of the following struct. -*/ -typedef struct RecoverTable RecoverTable; -struct RecoverTable { - char *zQuoted; /* Quoted version of table name */ - int nCol; /* Number of columns in table */ - char **azlCol; /* Array of column lists */ - int iPk; /* Index of IPK column */ -}; - -/* -** Free a RecoverTable object allocated by recoverFindTable() or -** recoverOrphanTable(). -*/ -static void recoverFreeTable(RecoverTable *pTab){ - if( pTab ){ - sqlite3_free(pTab->zQuoted); - if( pTab->azlCol ){ - int i; - for(i=0; i<=pTab->nCol; i++){ - sqlite3_free(pTab->azlCol[i]); - } - sqlite3_free(pTab->azlCol); - } - sqlite3_free(pTab); - } -} - -/* -** This function is a no-op if (*pRc) is not SQLITE_OK when it is called. -** Otherwise, it allocates and returns a RecoverTable object based on the -** final four arguments passed to this function. It is the responsibility -** of the caller to eventually free the returned object using -** recoverFreeTable(). -*/ -static RecoverTable *recoverNewTable( - int *pRc, /* IN/OUT: Error code */ - const char *zName, /* Name of table */ - const char *zSql, /* CREATE TABLE statement */ - int bIntkey, - int nCol -){ - sqlite3 *dbtmp = 0; /* sqlite3 handle for testing CREATE TABLE */ - int rc = *pRc; - RecoverTable *pTab = 0; - - pTab = (RecoverTable*)shellMalloc(&rc, sizeof(RecoverTable)); - if( rc==SQLITE_OK ){ - int nSqlCol = 0; - int bSqlIntkey = 0; - sqlite3_stmt *pStmt = 0; - - rc = sqlite3_open("", &dbtmp); - if( rc==SQLITE_OK ){ - sqlite3_create_function(dbtmp, "shell_idquote", 1, SQLITE_UTF8, 0, - shellIdQuote, 0, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_exec(dbtmp, "PRAGMA writable_schema = on", 0, 0, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_exec(dbtmp, zSql, 0, 0, 0); - if( rc==SQLITE_ERROR ){ - rc = SQLITE_OK; - goto finished; - } - } - shellPreparePrintf(dbtmp, &rc, &pStmt, - "SELECT count(*) FROM pragma_table_info(%Q)", zName - ); - if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - nSqlCol = sqlite3_column_int(pStmt, 0); - } - shellFinalize(&rc, pStmt); - - if( rc!=SQLITE_OK || nSqlColiPk to the index - ** of the column, where columns are 0-numbered from left to right. - ** Or, if this is a WITHOUT ROWID table or if there is no IPK column, - ** leave zPk as "_rowid_" and pTab->iPk at -2. */ - pTab->iPk = -2; - if( bIntkey ){ - shellPreparePrintf(dbtmp, &rc, &pPkFinder, - "SELECT cid, name FROM pragma_table_info(%Q) " - " WHERE pk=1 AND type='integer' COLLATE nocase" - " AND NOT EXISTS (SELECT cid FROM pragma_table_info(%Q) WHERE pk=2)" - , zName, zName - ); - if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPkFinder) ){ - pTab->iPk = sqlite3_column_int(pPkFinder, 0); - zPk = (const char*)sqlite3_column_text(pPkFinder, 1); - if( zPk==0 ){ zPk = "_"; /* Defensive. Should never happen */ } - } - } - - pTab->zQuoted = shellMPrintf(&rc, "\"%w\"", zName); - pTab->azlCol = (char**)shellMalloc(&rc, sizeof(char*) * (nSqlCol+1)); - pTab->nCol = nSqlCol; - - if( bIntkey ){ - pTab->azlCol[0] = shellMPrintf(&rc, "\"%w\"", zPk); - }else{ - pTab->azlCol[0] = shellMPrintf(&rc, ""); - } - i = 1; - shellPreparePrintf(dbtmp, &rc, &pStmt, - "SELECT %Q || group_concat(shell_idquote(name), ', ') " - " FILTER (WHERE cid!=%d) OVER (ORDER BY %s cid) " - "FROM pragma_table_info(%Q)", - bIntkey ? ", " : "", pTab->iPk, - bIntkey ? "" : "(CASE WHEN pk=0 THEN 1000000 ELSE pk END), ", - zName - ); - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - const char *zText = (const char*)sqlite3_column_text(pStmt, 0); - pTab->azlCol[i] = shellMPrintf(&rc, "%s%s", pTab->azlCol[0], zText); - i++; - } - shellFinalize(&rc, pStmt); - - shellFinalize(&rc, pPkFinder); - } - } - - finished: - sqlite3_close(dbtmp); - *pRc = rc; - if( rc!=SQLITE_OK || (pTab && pTab->zQuoted==0) ){ - recoverFreeTable(pTab); - pTab = 0; - } - return pTab; -} - -/* -** This function is called to search the schema recovered from the -** sqlite_schema table of the (possibly) corrupt database as part -** of a ".recover" command. Specifically, for a table with root page -** iRoot and at least nCol columns. Additionally, if bIntkey is 0, the -** table must be a WITHOUT ROWID table, or if non-zero, not one of -** those. -** -** If a table is found, a (RecoverTable*) object is returned. Or, if -** no such table is found, but bIntkey is false and iRoot is the -** root page of an index in the recovered schema, then (*pbNoop) is -** set to true and NULL returned. Or, if there is no such table or -** index, NULL is returned and (*pbNoop) set to 0, indicating that -** the caller should write data to the orphans table. -*/ -static RecoverTable *recoverFindTable( - ShellState *pState, /* Shell state object */ - int *pRc, /* IN/OUT: Error code */ - int iRoot, /* Root page of table */ - int bIntkey, /* True for an intkey table */ - int nCol, /* Number of columns in table */ - int *pbNoop /* OUT: True if iRoot is root of index */ -){ - sqlite3_stmt *pStmt = 0; - RecoverTable *pRet = 0; - int bNoop = 0; - const char *zSql = 0; - const char *zName = 0; - - /* Search the recovered schema for an object with root page iRoot. */ - shellPreparePrintf(pState->db, pRc, &pStmt, - "SELECT type, name, sql FROM recovery.schema WHERE rootpage=%d", iRoot - ); - while( *pRc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - const char *zType = (const char*)sqlite3_column_text(pStmt, 0); - if( bIntkey==0 && sqlite3_stricmp(zType, "index")==0 ){ - bNoop = 1; - break; - } - if( sqlite3_stricmp(zType, "table")==0 ){ - zName = (const char*)sqlite3_column_text(pStmt, 1); - zSql = (const char*)sqlite3_column_text(pStmt, 2); - if( zName!=0 && zSql!=0 ){ - pRet = recoverNewTable(pRc, zName, zSql, bIntkey, nCol); - break; - } - } - } - - shellFinalize(pRc, pStmt); - *pbNoop = bNoop; - return pRet; -} - -/* -** Return a RecoverTable object representing the orphans table. -*/ -static RecoverTable *recoverOrphanTable( - ShellState *pState, /* Shell state object */ - int *pRc, /* IN/OUT: Error code */ - const char *zLostAndFound, /* Base name for orphans table */ - int nCol /* Number of user data columns */ -){ - RecoverTable *pTab = 0; - if( nCol>=0 && *pRc==SQLITE_OK ){ - int i; - - /* This block determines the name of the orphan table. The prefered - ** name is zLostAndFound. But if that clashes with another name - ** in the recovered schema, try zLostAndFound_0, zLostAndFound_1 - ** and so on until a non-clashing name is found. */ - int iTab = 0; - char *zTab = shellMPrintf(pRc, "%s", zLostAndFound); - sqlite3_stmt *pTest = 0; - shellPrepare(pState->db, pRc, - "SELECT 1 FROM recovery.schema WHERE name=?", &pTest - ); - if( pTest ) sqlite3_bind_text(pTest, 1, zTab, -1, SQLITE_TRANSIENT); - while( *pRc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pTest) ){ - shellReset(pRc, pTest); - sqlite3_free(zTab); - zTab = shellMPrintf(pRc, "%s_%d", zLostAndFound, iTab++); - sqlite3_bind_text(pTest, 1, zTab, -1, SQLITE_TRANSIENT); - } - shellFinalize(pRc, pTest); - - pTab = (RecoverTable*)shellMalloc(pRc, sizeof(RecoverTable)); - if( pTab ){ - pTab->zQuoted = shellMPrintf(pRc, "\"%w\"", zTab); - pTab->nCol = nCol; - pTab->iPk = -2; - if( nCol>0 ){ - pTab->azlCol = (char**)shellMalloc(pRc, sizeof(char*) * (nCol+1)); - if( pTab->azlCol ){ - pTab->azlCol[nCol] = shellMPrintf(pRc, ""); - for(i=nCol-1; i>=0; i--){ - pTab->azlCol[i] = shellMPrintf(pRc, "%s, NULL", pTab->azlCol[i+1]); - } - } - } - - if( *pRc!=SQLITE_OK ){ - recoverFreeTable(pTab); - pTab = 0; - }else{ - raw_printf(pState->out, - "CREATE TABLE %s(rootpgno INTEGER, " - "pgno INTEGER, nfield INTEGER, id INTEGER", pTab->zQuoted - ); - for(i=0; iout, ", c%d", i); - } - raw_printf(pState->out, ");\n"); - } - } - sqlite3_free(zTab); - } - return pTab; +static int recoverSqlCb(void *pCtx, const char *zSql){ + ShellState *pState = (ShellState*)pCtx; + raw_printf(stdout, "%s;\n", zSql); + return SQLITE_OK; } /* @@ -7618,17 +7272,13 @@ static RecoverTable *recoverOrphanTable( */ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){ int rc = SQLITE_OK; - sqlite3_stmt *pLoop = 0; /* Loop through all root pages */ - sqlite3_stmt *pPages = 0; /* Loop through all pages in a group */ - sqlite3_stmt *pCells = 0; /* Loop through all cells in a page */ const char *zRecoveryDb = ""; /* Name of "recovery" database */ - const char *zLostAndFound = "lost_and_found"; - int i; - int nOrphan = -1; - RecoverTable *pOrphan = 0; - + const char *zLAF = "lost_and_found"; int bFreelist = 1; /* 0 if --freelist-corrupt is specified */ int bRowids = 1; /* 0 if --no-rowids */ + sqlite3_recover *p = 0; + int i = 0; + for(i=1; idb, &rc, - /* Attach an in-memory database named 'recovery'. Create an indexed - ** cache of the sqlite_dbptr virtual table. */ - "PRAGMA writable_schema = on;" - "ATTACH %Q AS recovery;" - "DROP TABLE IF EXISTS recovery.dbptr;" - "DROP TABLE IF EXISTS recovery.freelist;" - "DROP TABLE IF EXISTS recovery.map;" - "DROP TABLE IF EXISTS recovery.schema;" - "CREATE TABLE recovery.freelist(pgno INTEGER PRIMARY KEY);", zRecoveryDb + p = sqlite3_recover_init_sql( + pState->db, "main", recoverSqlCb, (void*)pState ); - if( bFreelist ){ - shellExec(pState->db, &rc, - "WITH trunk(pgno) AS (" - " SELECT shell_int32(" - " (SELECT data FROM sqlite_dbpage WHERE pgno=1), 8) AS x " - " WHERE x>0" - " UNION" - " SELECT shell_int32(" - " (SELECT data FROM sqlite_dbpage WHERE pgno=trunk.pgno), 0) AS x " - " FROM trunk WHERE x>0" - ")," - "freelist(data, n, freepgno) AS (" - " SELECT data, min(16384, shell_int32(data, 1)-1), t.pgno " - " FROM trunk t, sqlite_dbpage s WHERE s.pgno=t.pgno" - " UNION ALL" - " SELECT data, n-1, shell_int32(data, 2+n) " - " FROM freelist WHERE n>=0" - ")" - "REPLACE INTO recovery.freelist SELECT freepgno FROM freelist;" - ); + sqlite3_recover_config(p, SQLITE_RECOVER_TESTDB, (void*)zRecoveryDb); + sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF); + sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids); + sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist); + + sqlite3_recover_step(p); + if( sqlite3_recover_errcode(p)!=SQLITE_OK ){ + const char *zErr = sqlite3_recover_errmsg(p); + int errCode = sqlite3_recover_errcode(p); + raw_printf(stderr, "sql error: %s (%d)\n", zErr, errCode); } - - /* If this is an auto-vacuum database, add all pointer-map pages to - ** the freelist table. Do this regardless of whether or not - ** --freelist-corrupt was specified. */ - shellExec(pState->db, &rc, - "WITH ptrmap(pgno) AS (" - " SELECT 2 WHERE shell_int32(" - " (SELECT data FROM sqlite_dbpage WHERE pgno=1), 13" - " )" - " UNION ALL " - " SELECT pgno+1+(SELECT page_size FROM pragma_page_size)/5 AS pp " - " FROM ptrmap WHERE pp<=(SELECT page_count FROM pragma_page_count)" - ")" - "REPLACE INTO recovery.freelist SELECT pgno FROM ptrmap" - ); - - shellExec(pState->db, &rc, - "CREATE TABLE recovery.dbptr(" - " pgno, child, PRIMARY KEY(child, pgno)" - ") WITHOUT ROWID;" - "INSERT OR IGNORE INTO recovery.dbptr(pgno, child) " - " SELECT * FROM sqlite_dbptr" - " WHERE pgno NOT IN freelist AND child NOT IN freelist;" - - /* Delete any pointer to page 1. This ensures that page 1 is considered - ** a root page, regardless of how corrupt the db is. */ - "DELETE FROM recovery.dbptr WHERE child = 1;" - - /* Delete all pointers to any pages that have more than one pointer - ** to them. Such pages will be treated as root pages when recovering - ** data. */ - "DELETE FROM recovery.dbptr WHERE child IN (" - " SELECT child FROM recovery.dbptr GROUP BY child HAVING count(*)>1" - ");" - - /* Create the "map" table that will (eventually) contain instructions - ** for dealing with each page in the db that contains one or more - ** records. */ - "CREATE TABLE recovery.map(" - "pgno INTEGER PRIMARY KEY, maxlen INT, intkey, root INT" - ");" - - /* Populate table [map]. If there are circular loops of pages in the - ** database, the following adds all pages in such a loop to the map - ** as individual root pages. This could be handled better. */ - "WITH pages(i, maxlen) AS (" - " SELECT page_count, (" - " SELECT max(field+1) FROM sqlite_dbdata WHERE pgno=page_count" - " ) FROM pragma_page_count WHERE page_count>0" - " UNION ALL" - " SELECT i-1, (" - " SELECT max(field+1) FROM sqlite_dbdata WHERE pgno=i-1" - " ) FROM pages WHERE i>=2" - ")" - "INSERT INTO recovery.map(pgno, maxlen, intkey, root) " - " SELECT i, maxlen, NULL, (" - " WITH p(orig, pgno, parent) AS (" - " SELECT 0, i, (SELECT pgno FROM recovery.dbptr WHERE child=i)" - " UNION " - " SELECT i, p.parent, " - " (SELECT pgno FROM recovery.dbptr WHERE child=p.parent) FROM p" - " )" - " SELECT pgno FROM p WHERE (parent IS NULL OR pgno = orig)" - ") " - "FROM pages WHERE maxlen IS NOT NULL AND i NOT IN freelist;" - "UPDATE recovery.map AS o SET intkey = (" - " SELECT substr(data, 1, 1)==X'0D' FROM sqlite_dbpage WHERE pgno=o.pgno" - ");" - - /* Extract data from page 1 and any linked pages into table - ** recovery.schema. With the same schema as an sqlite_schema table. */ - "CREATE TABLE recovery.schema(type, name, tbl_name, rootpage, sql);" - "INSERT INTO recovery.schema SELECT " - " max(CASE WHEN field=0 THEN value ELSE NULL END)," - " max(CASE WHEN field=1 THEN value ELSE NULL END)," - " max(CASE WHEN field=2 THEN value ELSE NULL END)," - " max(CASE WHEN field=3 THEN value ELSE NULL END)," - " max(CASE WHEN field=4 THEN value ELSE NULL END)" - "FROM sqlite_dbdata WHERE pgno IN (" - " SELECT pgno FROM recovery.map WHERE root=1" - ")" - "GROUP BY pgno, cell;" - "CREATE INDEX recovery.schema_rootpage ON schema(rootpage);" - ); - - /* Open a transaction, then print out all non-virtual, non-"sqlite_%" - ** CREATE TABLE statements that extracted from the existing schema. */ - if( rc==SQLITE_OK ){ - sqlite3_stmt *pStmt = 0; - /* ".recover" might output content in an order which causes immediate - ** foreign key constraints to be violated. So disable foreign-key - ** constraint enforcement to prevent problems when running the output - ** script. */ - raw_printf(pState->out, "PRAGMA foreign_keys=OFF;\n"); - raw_printf(pState->out, "BEGIN;\n"); - raw_printf(pState->out, "PRAGMA writable_schema = on;\n"); - shellPrepare(pState->db, &rc, - "SELECT sql FROM recovery.schema " - "WHERE type='table' AND sql LIKE 'create table%'", &pStmt - ); - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - const char *zCreateTable = (const char*)sqlite3_column_text(pStmt, 0); - raw_printf(pState->out, "CREATE TABLE IF NOT EXISTS %s;\n", - &zCreateTable[12] - ); - } - shellFinalize(&rc, pStmt); - } - - /* Figure out if an orphan table will be required. And if so, how many - ** user columns it should contain */ - shellPrepare(pState->db, &rc, - "SELECT coalesce(max(maxlen), -2) FROM recovery.map WHERE root>1" - , &pLoop - ); - if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pLoop) ){ - nOrphan = sqlite3_column_int(pLoop, 0); - } - shellFinalize(&rc, pLoop); - pLoop = 0; - - shellPrepare(pState->db, &rc, - "SELECT pgno FROM recovery.map WHERE root=?", &pPages - ); - - shellPrepare(pState->db, &rc, - "SELECT max(field), group_concat(shell_escape_crnl(quote" - "(case when (? AND field<0) then NULL else value end)" - "), ', ')" - ", min(field) " - "FROM sqlite_dbdata WHERE pgno = ? AND field != ?" - "GROUP BY cell", &pCells - ); - - /* Loop through each root page. */ - shellPrepare(pState->db, &rc, - "SELECT root, intkey, max(maxlen) FROM recovery.map" - " WHERE root>1 GROUP BY root, intkey ORDER BY root=(" - " SELECT rootpage FROM recovery.schema WHERE name='sqlite_sequence'" - ")", &pLoop - ); - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pLoop) ){ - int iRoot = sqlite3_column_int(pLoop, 0); - int bIntkey = sqlite3_column_int(pLoop, 1); - int nCol = sqlite3_column_int(pLoop, 2); - int bNoop = 0; - RecoverTable *pTab; - - assert( bIntkey==0 || bIntkey==1 ); - pTab = recoverFindTable(pState, &rc, iRoot, bIntkey, nCol, &bNoop); - if( bNoop || rc ) continue; - if( pTab==0 ){ - if( pOrphan==0 ){ - pOrphan = recoverOrphanTable(pState, &rc, zLostAndFound, nOrphan); - } - pTab = pOrphan; - if( pTab==0 ) break; - } - - if( 0==sqlite3_stricmp(pTab->zQuoted, "\"sqlite_sequence\"") ){ - raw_printf(pState->out, "DELETE FROM sqlite_sequence;\n"); - } - sqlite3_bind_int(pPages, 1, iRoot); - if( bRowids==0 && pTab->iPk<0 ){ - sqlite3_bind_int(pCells, 1, 1); - }else{ - sqlite3_bind_int(pCells, 1, 0); - } - sqlite3_bind_int(pCells, 3, pTab->iPk); - - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPages) ){ - int iPgno = sqlite3_column_int(pPages, 0); - sqlite3_bind_int(pCells, 2, iPgno); - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pCells) ){ - int nField = sqlite3_column_int(pCells, 0); - int iMin = sqlite3_column_int(pCells, 2); - const char *zVal = (const char*)sqlite3_column_text(pCells, 1); - - RecoverTable *pTab2 = pTab; - if( pTab!=pOrphan && (iMin<0)!=bIntkey ){ - if( pOrphan==0 ){ - pOrphan = recoverOrphanTable(pState, &rc, zLostAndFound, nOrphan); - } - pTab2 = pOrphan; - if( pTab2==0 ) break; - } - - nField = nField+1; - if( pTab2==pOrphan ){ - raw_printf(pState->out, - "INSERT INTO %s VALUES(%d, %d, %d, %s%s%s);\n", - pTab2->zQuoted, iRoot, iPgno, nField, - iMin<0 ? "" : "NULL, ", zVal, pTab2->azlCol[nField] - ); - }else{ - raw_printf(pState->out, "INSERT INTO %s(%s) VALUES( %s );\n", - pTab2->zQuoted, pTab2->azlCol[nField], zVal - ); - } - } - shellReset(&rc, pCells); - } - shellReset(&rc, pPages); - if( pTab!=pOrphan ) recoverFreeTable(pTab); - } - shellFinalize(&rc, pLoop); - shellFinalize(&rc, pPages); - shellFinalize(&rc, pCells); - recoverFreeTable(pOrphan); - - /* The rest of the schema */ - if( rc==SQLITE_OK ){ - sqlite3_stmt *pStmt = 0; - shellPrepare(pState->db, &rc, - "SELECT sql, name FROM recovery.schema " - "WHERE sql NOT LIKE 'create table%'", &pStmt - ); - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - const char *zSql = (const char*)sqlite3_column_text(pStmt, 0); - if( sqlite3_strnicmp(zSql, "create virt", 11)==0 ){ - const char *zName = (const char*)sqlite3_column_text(pStmt, 1); - char *zPrint = shellMPrintf(&rc, - "INSERT INTO sqlite_schema VALUES('table', %Q, %Q, 0, %Q)", - zName, zName, zSql - ); - raw_printf(pState->out, "%s;\n", zPrint); - sqlite3_free(zPrint); - }else{ - raw_printf(pState->out, "%s;\n", zSql); - } - } - shellFinalize(&rc, pStmt); - } - - if( rc==SQLITE_OK ){ - raw_printf(pState->out, "PRAGMA writable_schema = off;\n"); - raw_printf(pState->out, "COMMIT;\n"); - } - sqlite3_exec(pState->db, "DETACH recovery", 0, 0, 0); + rc = sqlite3_recover_finish(p); return rc; } #endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */ From fa7098d5436a485c3ff4cf7e84e29a996c4fddd6 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 8 Sep 2022 19:53:59 +0000 Subject: [PATCH 066/428] Experimentally add --nomutex flag to speedtest1 to have it open db with the SQLITE_OPEN_NOMUTEX flag. FossilOrigin-Name: 45ef576b2ee6369ab1e3aa2ad284764bb334f92a69a33d88b7292bbc0a1b1fda --- manifest | 12 ++++++------ manifest.uuid | 2 +- test/speedtest1.c | 8 +++++++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 75eb7e14a6..e7aae4339d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sspeedtest1-worker.html,\san\sinteractive\sWorker-thread\svariant\sof\sspeedtest1.html.\sAdd\sext/wasm/index.html\sto\sact\sas\sa\sgateway\sto\sthe\svarious\stest\spages. -D 2022-09-08T15:30:59.731 +C Experimentally\sadd\s--nomutex\sflag\sto\sspeedtest1\sto\shave\sit\sopen\sdb\swith\sthe\sSQLITE_OPEN_NOMUTEX\sflag. +D 2022-09-08T19:53:59.576 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1499,7 +1499,7 @@ F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715 F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d1f0c -F test/speedtest1.c 55ef13e008cbb7dbea032478e60686599679b0b6192e44845c100fdabd14f8ff +F test/speedtest1.c 345566965466923762c1d7f29295d766a05c87bf48115b3030ceec7592cd1cca F test/spellfix.test 951a6405d49d1a23d6b78027d3877b4a33eeb8221dcab5704b499755bb4f552e F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3 F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33 @@ -2019,8 +2019,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 5ea0623630d769a8f3f07a40cd119be86b631192cdb5178131876b01b40ee5e0 -R b5dc0101db445e11af3bccae9adaef48 +P f16c68ee6d5ebb8dec2ab656dbab2ddb5f1d5133153ad553f986b31020adaa38 +R 7f356b1a33873f45424fcff13841218e U stephan -Z 2c78924308cfac29c905a5ead30d4718 +Z 0d8d2c17b5d2ef83d66b0d6f4d0cb65c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9c75a6fdf6..2d863e14be 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f16c68ee6d5ebb8dec2ab656dbab2ddb5f1d5133153ad553f986b31020adaa38 \ No newline at end of file +45ef576b2ee6369ab1e3aa2ad284764bb334f92a69a33d88b7292bbc0a1b1fda \ No newline at end of file diff --git a/test/speedtest1.c b/test/speedtest1.c index 3b8bd92e3b..980ee747d1 100644 --- a/test/speedtest1.c +++ b/test/speedtest1.c @@ -20,6 +20,7 @@ static const char zHelp[] = " --mmap SZ MMAP the first SZ bytes of the database file\n" " --multithread Set multithreaded mode\n" " --nomemstat Disable memory statistics\n" + " --nomutex Open db with SQLITE_OPEN_NOMUTEX\n" " --nosync Set PRAGMA synchronous=OFF\n" " --notnull Add NOT NULL constraints to table columns\n" " --output FILE Store SQL output in FILE\n" @@ -2192,6 +2193,8 @@ int main(int argc, char **argv){ int nThread = 0; /* --threads value */ int mmapSize = 0; /* How big of a memory map to use */ int memDb = 0; /* --memdb. Use an in-memory database */ + int openFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE + ; /* SQLITE_OPEN_xxx flags. */ char *zTSet = "main"; /* Which --testset torun */ int doTrace = 0; /* True for --trace */ const char *zEncoding = 0; /* --utf16be or --utf16le */ @@ -2272,6 +2275,8 @@ int main(int argc, char **argv){ if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); mmapSize = integerValue(argv[++i]); #endif + }else if( strcmp(z,"nomutex")==0 ){ + openFlags |= SQLITE_OPEN_NOMUTEX; }else if( strcmp(z,"nosync")==0 ){ noSync = 1; }else if( strcmp(z,"notnull")==0 ){ @@ -2413,7 +2418,8 @@ int main(int argc, char **argv){ sqlite3_initialize(); /* Open the database and the input file */ - if( sqlite3_open(memDb ? ":memory:" : zDbName, &g.db) ){ + if( sqlite3_open_v2(memDb ? ":memory:" : zDbName, &g.db, + openFlags, 0) ){ fatal_error("Cannot open database file: %s\n", zDbName); } #if SQLITE_VERSION_NUMBER>=3006001 From 1174c23e0dd8354a19d79c09dad8214c7ed85f02 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 8 Sep 2022 20:04:52 +0000 Subject: [PATCH 067/428] Minor build cleanups and tweaks in the speedtest1 wasm apps. FossilOrigin-Name: 5240fb4d795dea826f23cf5d2152b519f5a46f49bb2499ea868fa7c7f4ce788b --- ext/wasm/GNUmakefile | 6 +++--- ext/wasm/speedtest1-worker.html | 35 ++++++++++++++++++++++----------- ext/wasm/speedtest1.html | 2 +- manifest | 16 +++++++-------- manifest.uuid | 2 +- 5 files changed, 36 insertions(+), 25 deletions(-) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index a312bbdab4..576b8d320f 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -273,8 +273,8 @@ sqlite3-wasm.o := $(dir.api)/sqlite3-wasm.o $(sqlite3-wasm.o): emcc.cflags += $(SQLITE_OPT) $(sqlite3-wasm.o): $(dir.top)/sqlite3.c $(dir.api)/wasm_util.o: emcc.cflags += $(SQLITE_OPT) -sqlite3-wasm.c := $(dir.api)/sqlite3-wasm.c \ - $(dir.jacc)/jaccwabyt_test.c +sqlite3-wasm.c := $(dir.api)/sqlite3-wasm.c +jaccwabyt_test.c := $(dir.jacc)/jaccwabyt_test.c # ^^^ FIXME (how?): jaccwabyt_test.c is only needed for the test apps, # so we don't really want to include it in release builds. However, we # want to test the release builds with those apps, so we cannot simply @@ -290,7 +290,7 @@ $$($(1).o): $$(MAKEFILE) $(1) $$(emcc.bin) $$(emcc_opt) $$(emcc.flags) $$(emcc.cflags) -c $(1) -o $$@ CLEAN_FILES += $$($(1).o) endef -$(foreach c,$(sqlite3-wasm.c),$(eval $(call WASM_C_COMPILE,$(c)))) +$(foreach c,$(sqlite3-wasm.c) $(jaccwabyt_test.c),$(eval $(call WASM_C_COMPILE,$(c)))) $(sqlite3.js): $(sqlite3.js): $(MAKEFILE) $(sqlite3.wasm.obj) \ EXPORTED_FUNCTIONS.api \ diff --git a/ext/wasm/speedtest1-worker.html b/ext/wasm/speedtest1-worker.html index 60c475798c..f537ccacf7 100644 --- a/ext/wasm/speedtest1-worker.html +++ b/ext/wasm/speedtest1-worker.html @@ -50,12 +50,27 @@ -
+
+
+
+ Tips: +
    +
  • Control-click the flags to (de)select multiple flags.
  • +
  • The easiest way to try different optimization levels is, + from this directory: +
    $ rm -f speedtest1.js; make -e emcc_opt='-O2' speedtest1.js
    + Then reload this page. -O2 seems to consistently produce the fastest results. +
  • +
  • TODO: add a link in this app which launches the main-thread + version with the same flags.
  • +
+
diff --git a/ext/wasm/kvvfs1.js b/ext/wasm/kvvfs1.js index 169fcc8bdf..c29426fc63 100644 --- a/ext/wasm/kvvfs1.js +++ b/ext/wasm/kvvfs1.js @@ -19,12 +19,25 @@ const toss = function(...args){throw new Error(args.join(' '))}; const debug = console.debug.bind(console); const eOutput = document.querySelector('#test-output'); - const log = console.log.bind(console) - const logHtml = function(...args){ - log.apply(this, args); + const logC = console.log.bind(console) + const logE = function(domElement){ + eOutput.append(domElement); + }; + const logHtml = function(cssClass,...args){ const ln = document.createElement('div'); + if(cssClass) ln.classList.add(cssClass); ln.append(document.createTextNode(args.join(' '))); - eOutput.append(ln); + logE(ln); + } + const log = function(...args){ + logC(...args); + logHtml('',...args); + }; + const warn = function(...args){ + logHtml('warning',...args); + }; + const error = function(...args){ + logHtml('error',...args); }; const runTests = function(Module){ @@ -34,10 +47,14 @@ oo = sqlite3.oo1, wasm = capi.wasm; log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); - log("Build options:",wasm.compileOptionUsed()); T.assert( 0 !== capi.sqlite3_vfs_find(null) ); - + if(!oo.DB.clearKvvfsStorage){ + warn("This build is not kvvfs-capable."); + return; + } + const dbStorage = 1 ? ':sessionStorage:' : ':localStorage:'; + const theStore = 's'===dbStorage[1] ? sessionStorage : localStorage; /** The names ':sessionStorage:' and ':localStorage:' are handled via the DB class constructor, not the C level. In the C API, @@ -45,24 +62,52 @@ names for those keys, but that is subject to change. */ const db = new oo.DB( dbStorage ); - log("Storage backend:",db.filename /* note that the name was internally translated */); - try { - db.exec("create table if not exists t(a)"); - if(undefined===db.selectValue("select a from t limit 1")){ - log("New db. Populating. This DB will persist across page reloads."); - db.exec("insert into t(a) values(1),(2),(3)"); - }else{ - log("Found existing table data:"); + + document.querySelector('#btn-clear-storage').addEventListener('click',function(){ + oo.DB.clearKvvfsStorage(); + log("kvvfs localStorage and sessionStorage cleared."); + }); + document.querySelector('#btn-clear-log').addEventListener('click',function(){ + eOutput.innerText = ''; + }); + document.querySelector('#btn-init-db').addEventListener('click',function(){ + const saveSql = []; + try{ + db.exec({ + sql:["drop table if exists t;", + "create table if not exists t(a);", + "insert into t(a) values(?),(?),(?)"], + bind: [performance.now() >> 0, + (performance.now() * 2) >> 0, + (performance.now() / 2) >> 0], + saveSql + }); + console.log("saveSql =",saveSql,theStore); + log("DB (re)initialized."); + }catch(e){ + error(e.message); + } + }); + const btnSelect = document.querySelector('#btn-select1'); + btnSelect.addEventListener('click',function(){ + log("DB rows:"); + try{ db.exec({ sql: "select * from t order by a", rowMode: 0, - callback: function(v){log(v)} + callback: (v)=>log(v) }); + }catch(e){ + error(e.message); } - }finally{ - db.close(); + }); + log("Storage backend:",db.filename /* note that the name was internally translated */); + if(0===db.selectValue('select count(*) from sqlite_master')){ + log("DB is empty. Use the init button to populate it."); + }else{ + log("DB contains data from a previous session. Use the Clear Ctorage button to delete it."); + btnSelect.click(); } - log("End of demo."); }; sqlite3InitModule(self.sqlite3TestModule).then(function(theModule){ diff --git a/manifest b/manifest index c182819a97..f11a02ef2a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\skv-vfs\sbranch\sinto\sfiddle-opfs\sbranch\sto\sadd\skvvfs-based\swasm\sbuild\sand\sdemo. -D 2022-09-12T16:09:50.625 +C Add\ssqlite3.oo1.DB.clearKvvfsStorage().\sAdd\scontrols\sto\skvvfs1.js\sdemo\sto\sreset\sand\squery\sthe\sdb\swithout\srequiring\sthe\sdev\sconsole. +D 2022-09-12T17:59:04.237 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -483,7 +483,7 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 101919ec261644e2f6f0a59952fd9612127b69ea99b493277b2789ea478f9b6b F ext/wasm/api/sqlite3-api-glue.js 2bf536a38cde324cf352bc2c575f8e22c6d204d667c0eda5a254ba45318914bc -F ext/wasm/api/sqlite3-api-oo1.js a9d8892be246548a9978ace506d108954aa13eb5ce25332975c8377953804ff3 +F ext/wasm/api/sqlite3-api-oo1.js b498662748918c132aa59433ea2bd2ebb7e026549fd68506a1ae1ea94736f4f6 F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 F ext/wasm/api/sqlite3-api-prologue.js 9e37ce4dfd74926d0df80dd7e72e33085db4bcee48e2c21236039be416a7dff2 F ext/wasm/api/sqlite3-api-worker1.js d33062afa045fd4be01ba4abc266801807472558b862b30056211b00c9c347b4 @@ -507,8 +507,8 @@ F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356a F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19 F ext/wasm/kvvfs.make dba616578bf91a76370a46494dd68a09c6dff5beb6d5561e2db65a27216e9630 -F ext/wasm/kvvfs1.html b8304cd5c7e7ec32c3b15521a95c322d6efdb8d22b3c4156123545dc54e07583 -F ext/wasm/kvvfs1.js a5075f98ffecd7d32348697db991fc61342d89aa20651034d1572af61890fb8b +F ext/wasm/kvvfs1.html 83bac238d1e93ed102a461672fc58fe74e611e426708e9a5c66002c01eadb942 +F ext/wasm/kvvfs1.js 53721a42e0ec45f6978cc723e5de47f882517867d0fcff4c6ff05f4c422e27c4 F ext/wasm/scratchpad-opfs-main.html 4565cf194e66188190d35f70e82553e2e2d72b9809b73c94ab67b8cfd14d2e0c F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 @@ -596,14 +596,14 @@ F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 F src/os_kv.c a188e92dac693b1c1b512d93b0c4dc85c1baad11e322b01121f87057996e4d11 F src/os_setup.h 0711dbc4678f3ac52d7fe736951b6384a0615387c4ba5135a4764e4e31f4b6a6 F src/os_unix.c d6322b78130d995160bb9cfb7850678ad6838b08c1d13915461b33326a406c04 -F src/os_win.c e9454cb141908e8eef2102180bad353a36480612d5b736e4c2bd5777d9b25a34 +F src/os_win.c 8d129ae3e59e0fa900e20d0ad789e96f2e08177f0b00b53cdda65c40331e0902 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 6176d9752eb580419e8fef4592dc417a6b00ddfd43ee22f818819bf8840ceee8 F src/pager.h f82e9844166e1585f5786837ddc7709966138ced17f568c16af7ccf946c2baa3 F src/parse.y 8e67d820030d2655b9942ffe61c1e7e6b96cea2f2f72183533299393907d0564 -F src/pcache.c 22a6ebe498d1d26c85fd1e3bcb246d97b882c060027c1e1688fbea905f5ac3cf +F src/pcache.c f4268f7f73c6a3db12ce22fd25bc68dc42315d19599414ab1207d7cf32f79197 F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586 -F src/pcache1.c 849a26ea9dc1e6a176b75dc576672a598170b0b46aeef87a981dd25e0af0ccf9 +F src/pcache1.c dee95e3cd2b61e6512dc814c5ab76d5eb36f0bfc9441dbb4260fccc0d12bbddc F src/pragma.c 9bf7d8a2a9ad3bc36df3ec0d61817a44c38a1da527d59c26c203047f906e334a F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c 971d5819a4bda88038c2283d71fc0a2974ffc3dd480f9bd941341017abacfd1b @@ -1981,7 +1981,7 @@ F tool/showstat4.c 0682ebea7abf4d3657f53c4a243f2e7eab48eab344ed36a94bb75dcd19a5c F tool/showwal.c 0253c187ae16fdae9cde89e63e1dfcd3bb35e5416d066415f99e2f8cac6ab03d F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe F tool/spaceanal.tcl 1b5be34c6223cb1af06da2a10fb77863eb869b1962d055820b0a11cf2336ab45 -F tool/speed-check.sh ff74a68bb95a0341275f4d3c9a7d8a3800bd278aceecf1913295a1f0175bc3e6 +F tool/speed-check.sh 13f8e07dbfe25f3aecda33fb6068894665af61ca1360a7b654be0ad0c3f3ae0b F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355 F tool/speedtest16.c ecb6542862151c3e6509bbc00509b234562ae81e F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff @@ -2023,8 +2023,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 4e6ce329872eb733ba2f7f7879747c52761ae97790fd8ed169a25a79854cc3d9 e49682c5eac91958f143e639c5656ca54560d14f5805d514bf4aa0c206e63844 -R f15733833cf5e73dce86b4365f218581 +P a7d8b26acd3c1ae344369e4d70804c0cab45272c0983cfd32d616a0a7b28acb9 +R 984d02350bb7056e7ff7dbf797b44085 U stephan -Z f2a01bf4c99986993a2e00b39e93be73 +Z f41406c18c315f5b1f10f4c607ad1f16 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bde2102af8..c3f7434826 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a7d8b26acd3c1ae344369e4d70804c0cab45272c0983cfd32d616a0a7b28acb9 \ No newline at end of file +d845c6c22bd5d3fffc66e0566df346d690dd8bd1fc1688e312161b1a1edcfd79 \ No newline at end of file diff --git a/src/os_win.c b/src/os_win.c index 2827b0b49b..d71fb39220 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -5763,7 +5763,8 @@ static int winFullPathname( char *zFull /* Output buffer */ ){ int rc; - sqlite3_mutex *pMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR); + MUTEX_LOGIC( sqlite3_mutex *pMutex; ) + MUTEX_LOGIC( pMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR); ) sqlite3_mutex_enter(pMutex); rc = winFullPathnameNoMutex(pVfs, zRelative, nFull, zFull); sqlite3_mutex_leave(pMutex); diff --git a/src/pcache.c b/src/pcache.c index 8c57f5b1eb..0407e06b2f 100644 --- a/src/pcache.c +++ b/src/pcache.c @@ -655,14 +655,14 @@ void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){ assert( sqlite3PcachePageSanity(p) ); pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno)); pOther = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, newPgno, 0); - sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno); if( pOther ){ - PgHdr *pPg = (PgHdr*)pOther->pExtra; - pPg->pgno = p->pgno; - if( pPg->pPage==0 ){ - sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pOther, 0); - } + PgHdr *pXPage = (PgHdr*)pOther->pExtra; + assert( pXPage->nRef==0 ); + pXPage->nRef++; + pCache->nRefSum++; + sqlite3PcacheDrop(pXPage); } + sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno); p->pgno = newPgno; if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); diff --git a/src/pcache1.c b/src/pcache1.c index a47087fa11..adbe953959 100644 --- a/src/pcache1.c +++ b/src/pcache1.c @@ -1125,7 +1125,7 @@ static void pcache1Rekey( assert( iOld!=iNew ); /* The page number really is changing */ pcache1EnterMutex(pCache->pGroup); - + assert( pcache1FetchNoMutex(p, iOld, 0)==pPage ); /* pPg really is iOld */ hOld = iOld%pCache->nHash; pp = &pCache->apHash[hOld]; @@ -1134,23 +1134,8 @@ static void pcache1Rekey( } *pp = pPage->pNext; + assert( pcache1FetchNoMutex(p, iNew, 0)==0 ); /* iNew not in cache */ hNew = iNew%pCache->nHash; - pp = &pCache->apHash[hNew]; - while( *pp ){ - if( (*pp)->iKey==iNew ){ - /* If there is already another pcache entry at iNew, change it to iOld, - ** thus swapping the positions of iNew and iOld */ - PgHdr1 *pOld = *pp; - *pp = pOld->pNext; - pOld->pNext = pCache->apHash[hOld]; - pCache->apHash[hOld] = pOld; - pOld->iKey = iOld; - break; - }else{ - pp = &(*pp)->pNext; - } - } - pPage->iKey = iNew; pPage->pNext = pCache->apHash[hNew]; pCache->apHash[hNew] = pPage; diff --git a/tool/speed-check.sh b/tool/speed-check.sh index 6b0fbeb43a..879ffa9562 100644 --- a/tool/speed-check.sh +++ b/tool/speed-check.sh @@ -96,6 +96,9 @@ while test "$1" != ""; do --cachesize) shift; SPEEDTEST_OPTS="$SPEEDTEST_OPTS --cachesize $1" ;; + --stmtcache) + shift; SPEEDTEST_OPTS="$SPEEDTEST_OPTS --stmtcache $1" + ;; --checkpoint) SPEEDTEST_OPTS="$SPEEDTEST_OPTS --checkpoint" ;; @@ -143,6 +146,9 @@ while test "$1" != ""; do SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset rtree" CC_OPTS="$CC_OPTS -DSQLITE_ENABLE_RTREE" ;; + --persist) + SPEEDTEST_OPTS="$SPEEDTEST_OPTS --persist" + ;; --orm) SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset orm" ;; From bb195124382c934f635fa9634feb9543c9dd4af2 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 12 Sep 2022 18:10:41 +0000 Subject: [PATCH 092/428] Fix a debugging/testing edit in the previous check-in. FossilOrigin-Name: 6fc8d34c0ae1f8277544be741f2f0835fad8e475d35bd24573224ccc1699b8bd --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/os_kv.c | 5 +++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index ed3fd61f48..06fc95c9d8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Faster\stext\sdecoder\sfor\skv_os.c. -D 2022-09-12T17:44:35.983 +C Fix\sa\sdebugging/testing\sedit\sin\sthe\sprevious\scheck-in. +D 2022-09-12T18:10:41.031 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -574,7 +574,7 @@ F src/notify.c 89a97dc854c3aa62ad5f384ef50c5a4a11d70fcc69f86de3e991573421130ed6 F src/os.c 0eb831ba3575af5277e47f4edd14fdfc90025c67eb25ce5cda634518d308d4e9 F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 -F src/os_kv.c 3f4c2f56375d50b210ddadfbfd652001c63e8b8138b8b2c9339e32d610107558 +F src/os_kv.c 226d9051af316dc547a37629b269ba2aebff8fa21915b91a9b41f7872bb4cc05 F src/os_setup.h 0711dbc4678f3ac52d7fe736951b6384a0615387c4ba5135a4764e4e31f4b6a6 F src/os_unix.c d6322b78130d995160bb9cfb7850678ad6838b08c1d13915461b33326a406c04 F src/os_win.c e9454cb141908e8eef2102180bad353a36480612d5b736e4c2bd5777d9b25a34 @@ -2004,8 +2004,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e49682c5eac91958f143e639c5656ca54560d14f5805d514bf4aa0c206e63844 -R 81ed8ce798a46ae2544ed3d032a07983 +P 3354a2edb762d70ccc31d4d25f81b70e644d76e01cb1e81d2e97b5414d809d30 +R 742e0e4f6e4e5630d202911ef2cdc14a U drh -Z 85de880a922a0287811e4245e97cc473 +Z 0a81f46489fd3e0c2c270769139a2009 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e0ded92cce..bf2f87a85b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3354a2edb762d70ccc31d4d25f81b70e644d76e01cb1e81d2e97b5414d809d30 \ No newline at end of file +6fc8d34c0ae1f8277544be741f2f0835fad8e475d35bd24573224ccc1699b8bd \ No newline at end of file diff --git a/src/os_kv.c b/src/os_kv.c index 6ecec6309c..ce0a571965 100644 --- a/src/os_kv.c +++ b/src/os_kv.c @@ -1042,8 +1042,9 @@ static int kvvfsFullPathname( char *zOut ){ size_t nPath; - -zPath = "local"; +#ifdef SQLITE_OS_KV_ALWAYS_LOCAL + zPath = "local"; +#endif nPath = strlen(zPath); SQLITE_KV_LOG(("xFullPathname(\"%s\")\n", zPath)); if( nOut Date: Mon, 12 Sep 2022 19:23:50 +0000 Subject: [PATCH 093/428] More tests for the recover module. FossilOrigin-Name: 37fb093b95c6b7d7ad07a275697df73b69f9fb5c5549aea8544b26e38f24833f --- ext/recover/recovercorrupt.test | 68 ++++++++++++++++++++++ ext/recover/recoverfault.test | 72 +++++++++++++++++++++++ ext/recover/sqlite3recover.c | 100 +++++++++++++++++++------------- ext/recover/test_recover.c | 4 +- manifest | 16 ++--- manifest.uuid | 2 +- 6 files changed, 211 insertions(+), 51 deletions(-) create mode 100644 ext/recover/recovercorrupt.test create mode 100644 ext/recover/recoverfault.test diff --git a/ext/recover/recovercorrupt.test b/ext/recover/recovercorrupt.test new file mode 100644 index 0000000000..ff1d2af19f --- /dev/null +++ b/ext/recover/recovercorrupt.test @@ -0,0 +1,68 @@ +# 2022 August 28 +# +# 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. +# +#*********************************************************************** +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] recover_common.tcl] +source $testdir/tester.tcl + +set testprefix recovercorrupt + +do_execsql_test 1.0 { + PRAGMA page_size = 512; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); + INSERT INTO t1 VALUES(1, 2, 3); + INSERT INTO t1 VALUES(2, hex(randomblob(100)), randomblob(200)); + CREATE INDEX i1 ON t1(b, c); + CREATE TABLE t2(a PRIMARY KEY, b, c) WITHOUT ROWID; + INSERT INTO t2 VALUES(1, 2, 3); + INSERT INTO t2 VALUES(2, hex(randomblob(100)), randomblob(200)); + ANALYZE; + PRAGMA writable_schema = 1; + DELETE FROM sqlite_schema WHERE name='t2'; +} + +do_test 1.1 { + file size test.db +} {5120} + +proc toggle_bit {blob bit} { + set byte [expr {$bit / 8}] + set bit [expr {$bit & 0x0F}] + binary scan $blob a${byte}ca* A x B + set x [expr {$x ^ (1 << $bit)}] + binary format a*ca* $A $x $B +} + + +db_save_and_close +for {set ii 200} {$ii < 10000} {incr ii} { + db_restore_and_reopen + db func toggle_bit toggle_bit + set pg [expr {($ii / 512)+1}] + set byte [expr {$ii % 512}] + db eval { + UPDATE sqlite_dbpage SET data = toggle_bit(data, $byte) WHERE pgno=$pg + } + + do_test 1.2.$ii { + set R [sqlite3_recover_init db main test.db2] + $R config lostandfound lost_and_found + $R step + $R finish + } {} +} + + +finish_test + diff --git a/ext/recover/recoverfault.test b/ext/recover/recoverfault.test new file mode 100644 index 0000000000..b2411fb6c7 --- /dev/null +++ b/ext/recover/recoverfault.test @@ -0,0 +1,72 @@ +# 2022 August 28 +# +# 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. +# +#*********************************************************************** +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] recover_common.tcl] +source $testdir/tester.tcl + +set testprefix recoverfault + + +#-------------------------------------------------------------------------- +proc compare_result {db1 db2 sql} { + set r1 [$db1 eval $sql] + set r2 [$db2 eval $sql] + if {$r1 != $r2} { + puts "r1: $r1" + puts "r2: $r2" + error "mismatch for $sql" + } + return "" +} + +proc compare_dbs {db1 db2} { + compare_result $db1 $db2 "SELECT sql FROM sqlite_master ORDER BY 1" + foreach tbl [$db1 eval {SELECT name FROM sqlite_master WHERE type='table'}] { + compare_result $db1 $db2 "SELECT * FROM $tbl" + } +} +#-------------------------------------------------------------------------- + +do_execsql_test 1.0 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); + INSERT INTO t1 VALUES(1, 2, 3); + INSERT INTO t1 VALUES(2, hex(randomblob(1000)), randomblob(2000)); + CREATE INDEX i1 ON t1(b, c); + CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c); + INSERT INTO t2 VALUES(1, 2, 3); + INSERT INTO t2 VALUES(2, hex(randomblob(1000)), randomblob(2000)); + ANALYZE; + PRAGMA writable_schema = 1; + DELETE FROM sqlite_schema WHERE name='t2'; +} + +do_faultsim_test 1 -faults oom* -prep { + faultsim_restore_and_reopen +} -body { + set R [sqlite3_recover_init db main test.db2] + $R config lostandfound lost_and_found + $R step + $R finish +} -test { + faultsim_test_result {0 {}} {1 {}} + if {$testrc==0} { + sqlite3 db2 test.db2 + compare_dbs db db2 + db2 close + } +} + +finish_test + diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index 9c5e10433b..849c3bc801 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -226,12 +226,13 @@ static int recoverError( int errCode, const char *zFmt, ... ){ + char *z = 0; va_list ap; - char *z; va_start(ap, zFmt); - z = sqlite3_vmprintf(zFmt, ap); - va_end(ap); - + if( zFmt ){ + z = sqlite3_vmprintf(zFmt, ap); + va_end(ap); + } sqlite3_free(p->zErrMsg); p->zErrMsg = z; p->errCode = errCode; @@ -448,7 +449,8 @@ static i64 recoverPageCount(sqlite3_recover *p){ if( p->errCode==SQLITE_OK ){ sqlite3_stmt *pStmt = 0; pStmt = recoverPreparePrintf(p, p->dbIn, "PRAGMA %Q.page_count", p->zDb); - if( pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){ + if( pStmt ){ + sqlite3_step(pStmt); nPg = sqlite3_column_int64(pStmt, 0); } recoverFinalize(p, pStmt); @@ -866,11 +868,11 @@ static void recoverAddTable( pNew->pNext = p->pTblList; p->pTblList = pNew; + pNew->bIntkey = 1; } recoverFinalize(p, pStmt); - pNew->bIntkey = 1; pStmt = recoverPreparePrintf(p, p->dbOut, "PRAGMA index_xinfo(%Q)", zName); while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ int iField = sqlite3_column_int(pStmt, 0); @@ -884,10 +886,12 @@ static void recoverAddTable( } recoverFinalize(p, pStmt); - if( iPk>=0 ){ - pNew->aCol[iPk].bIPK = 1; - }else if( pNew->bIntkey ){ - pNew->iRowidBind = iBind++; + if( p->errCode==SQLITE_OK ){ + if( iPk>=0 ){ + pNew->aCol[iPk].bIPK = 1; + }else if( pNew->bIntkey ){ + pNew->iRowidBind = iBind++; + } } } } @@ -1068,6 +1072,7 @@ static sqlite3_stmt *recoverInsertStmt( RecoverTable *pTab, int nField ){ + sqlite3_stmt *pRet = 0; const char *zSep = ""; const char *zSqlSep = ""; char *zSql = 0; @@ -1075,7 +1080,8 @@ static sqlite3_stmt *recoverInsertStmt( char *zBind = 0; int ii; int bSql = p->xSql ? 1 : 0; - sqlite3_stmt *pRet = 0; + + if( nField<=0 ) return 0; assert( nField<=pTab->nCol ); @@ -1525,7 +1531,7 @@ static int recoverWriteData(sqlite3_recover *p){ int bNewCell = (iPrevPage!=iPage || iPrevCell!=iCell); assert( bNewCell==0 || (iField==-1 || iField==0) ); - assert( bNewCell || iField==nVal ); + assert( bNewCell || iField==nVal || nVal==pTab->nCol ); if( bNewCell ){ if( nVal>=0 ){ @@ -1538,29 +1544,31 @@ static int recoverWriteData(sqlite3_recover *p){ pInsert = recoverInsertStmt(p, pTab, nVal); nInsert = nVal; } + if( nVal>0 ){ + for(ii=0; iinCol; ii++){ + RecoverColumn *pCol = &pTab->aCol[ii]; - for(ii=0; iinCol; ii++){ - RecoverColumn *pCol = &pTab->aCol[ii]; - - if( pCol->iBind>0 ){ - if( pCol->bIPK ){ - sqlite3_bind_int64(pInsert, pCol->iBind, iRowid); - }else if( pCol->iFieldiBind,apVal[pCol->iField]); + if( pCol->iBind>0 ){ + int iBind = pCol->iBind; + if( pCol->bIPK ){ + sqlite3_bind_int64(pInsert, iBind, iRowid); + }else if( pCol->iFieldiField]); + } } } - } - if( p->bRecoverRowid && pTab->iRowidBind>0 && bHaveRowid ){ - sqlite3_bind_int64(pInsert, pTab->iRowidBind, iRowid); - } + if( p->bRecoverRowid && pTab->iRowidBind>0 && bHaveRowid ){ + sqlite3_bind_int64(pInsert, pTab->iRowidBind, iRowid); + } - if( SQLITE_ROW==sqlite3_step(pInsert) && p->xSql ){ - const char *zSql = (const char*)sqlite3_column_text(pInsert, 0); - recoverSqlCallback(p, zSql); + if( SQLITE_ROW==sqlite3_step(pInsert) && p->xSql ){ + const char *z = (const char*)sqlite3_column_text(pInsert, 0); + recoverSqlCallback(p, z); + } + recoverReset(p, pInsert); + assert( p->errCode || pInsert ); + if( pInsert ) sqlite3_clear_bindings(pInsert); } - recoverReset(p, pInsert); - assert( p->errCode || pInsert ); - if( pInsert ) sqlite3_clear_bindings(pInsert); } for(ii=0; iinCol ){ assert( apVal[iField]==0 ); apVal[iField] = sqlite3_value_dup( pVal ); + if( apVal[iField]==0 ){ + recoverError(p, SQLITE_NOMEM, 0); + } nVal = iField+1; } iPrevCell = iCell; @@ -1686,7 +1697,6 @@ sqlite3_recover *recoverInit( int nByte = 0; if( zDb==0 ){ zDb = "main"; } - if( zUri==0 ){ zUri = ""; } nDb = recoverStrlen(zDb); nUri = recoverStrlen(zUri); @@ -1699,7 +1709,7 @@ sqlite3_recover *recoverInit( pRet->zDb = (char*)&pRet[1]; pRet->zUri = &pRet->zDb[nDb+1]; memcpy(pRet->zDb, zDb, nDb); - memcpy(pRet->zUri, zUri, nUri); + if( nUri>0 ) memcpy(pRet->zUri, zUri, nUri); pRet->xSql = xSql; pRet->pSqlCtx = pSqlCtx; pRet->bRecoverRowid = RECOVER_ROWID_DEFAULT; @@ -1730,14 +1740,14 @@ sqlite3_recover *sqlite3_recover_init_sql( int (*xSql)(void*, const char*), void *pSqlCtx ){ - return recoverInit(db, zDb, "", xSql, pSqlCtx); + return recoverInit(db, zDb, 0, xSql, pSqlCtx); } /* ** Return the handle error message, if any. */ const char *sqlite3_recover_errmsg(sqlite3_recover *p){ - return p ? p->zErrMsg : "not an error"; + return (p && p->errCode!=SQLITE_NOMEM) ? p->zErrMsg : "out of memory"; } /* @@ -1753,6 +1763,7 @@ int sqlite3_recover_errcode(sqlite3_recover *p){ int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){ int rc = SQLITE_OK; + if( p==0 ) return SQLITE_NOMEM; switch( op ){ case 789: sqlite3_free(p->zStateDb); @@ -1791,8 +1802,12 @@ int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){ */ int sqlite3_recover_run(sqlite3_recover *p){ if( p ){ + recoverExec(p, p->dbIn, "PRAGMA writable_schema=1"); if( p->bRun ) return SQLITE_MISUSE; /* Has already run */ if( p->errCode==SQLITE_OK ) recoverRun(p); + if( sqlite3_exec(p->dbIn, "PRAGMA writable_schema=0", 0, 0, 0) ){ + recoverDbError(p, p->dbIn); + } } return p ? p->errCode : SQLITE_NOMEM; } @@ -1807,11 +1822,16 @@ int sqlite3_recover_run(sqlite3_recover *p){ ** not been called on this handle. */ int sqlite3_recover_finish(sqlite3_recover *p){ - int rc = p->errCode; - sqlite3_free(p->zErrMsg); - sqlite3_free(p->zStateDb); - sqlite3_free(p->zLostAndFound); - sqlite3_free(p); + int rc; + if( p==0 ){ + rc = SQLITE_NOMEM; + }else{ + rc = p->errCode; + sqlite3_free(p->zErrMsg); + sqlite3_free(p->zStateDb); + sqlite3_free(p->zLostAndFound); + sqlite3_free(p); + } return rc; } diff --git a/ext/recover/test_recover.c b/ext/recover/test_recover.c index a31ddbb94a..a7c5afd3aa 100644 --- a/ext/recover/test_recover.c +++ b/ext/recover/test_recover.c @@ -165,9 +165,7 @@ static int testRecoverCmd( int res2; if( res!=SQLITE_OK ){ const char *zErr = sqlite3_recover_errmsg(pTest->p); - char *zRes = sqlite3_mprintf("(%d) - %s", res, zErr); - Tcl_SetObjResult(interp, Tcl_NewStringObj(zRes, -1)); - sqlite3_free(zRes); + Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1)); } res2 = sqlite3_recover_finish(pTest->p); assert( res2==res ); diff --git a/manifest b/manifest index d505905e12..c61b4a13ae 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Rework\srecover\sextension\scode\sfor\sreadability. -D 2022-09-10T20:01:49.308 +C More\stests\sfor\sthe\srecover\smodule. +D 2022-09-12T19:23:50.588 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -390,11 +390,13 @@ F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f5974282 F ext/recover/recover1.test 942016356f9098ca36933536b194b5878827a3a749e0bf41a83d83530c0d0ea8 F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c F ext/recover/recoverclobber.test e6537ebf99f57bfff6cca59550b5f4278319b57a89865abb98d755a8fd561d84 +F ext/recover/recovercorrupt.test 115cdb67ac29b4e8ec786cee9190ced674f62388f126b20deea22fa5fd11b814 +F ext/recover/recoverfault.test 30e3d1b423b33b4c57f4a97f9754b2d493a411d858b2672ab369d65199bc2420 F ext/recover/recoverold.test f368a6ae2db12b6017257b332a19ab5df527f4061e43f12f5c85d8e2b236f074 F ext/recover/recoverrowid.test ec4436cd69e6cdacb48dd2963ff6dd9dbd5fe648376de5e7c0c2f4f6cbacb417 -F ext/recover/sqlite3recover.c 4ed53fd33639ede83505f4397b8e1e46b6c0c5a5188c42f28f8372487141170a +F ext/recover/sqlite3recover.c d40d7c68a118e01d2b06c96325bbe3b85701f3add2ab5aeec289eeafd5e36d6c F ext/recover/sqlite3recover.h 81108efb8c4618d3d9c6da4df785212b0e4501aa0d25edfc463405fe839a6640 -F ext/recover/test_recover.c ed8d0cc8703ab29cf562f793623b045de109b7937f254108ff4132f35abb37fb +F ext/recover/test_recover.c 8f5ef0c9b7523c41a393f65e44d727c23cda8f44d5180fff5b698ee068ba538d F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 F ext/repair/checkfreelist.c e21f06995ff4efdc1622dcceaea4dcba2caa83ca2f31a1607b98a8509168a996 F ext/repair/checkindex.c 4383e4469c21e5b9ae321d0d63cec53e981af9d7a6564be6374f0eeb93dfc890 @@ -2007,8 +2009,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 599d1f8ec2f9e24924a6f9e66c85664360c7b95531b07a4efe1dd8c096b3fc99 -R cd56b05532a91c08b9e673fffba829f5 +P 1a2540960e40e3c8c622448fd3862e249bd463c29ae4ce5e39942e942533f60a +R 91d8f87cd1493a401d98e4544c1e3f6e U dan -Z 624cfda4b7c658530a41a87671068e55 +Z d3a21f62871d8e066c6c2bd48e5f7060 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 98f37f0563..340320e0cf 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1a2540960e40e3c8c622448fd3862e249bd463c29ae4ce5e39942e942533f60a \ No newline at end of file +37fb093b95c6b7d7ad07a275697df73b69f9fb5c5549aea8544b26e38f24833f \ No newline at end of file From d8bb0b5ae61221dd26a336fe103b8a98bd8fc757 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 12 Sep 2022 19:29:34 +0000 Subject: [PATCH 094/428] Add a gettimeofday()-based implementation of xCurrentTimeInt64() to os_kv.c. FossilOrigin-Name: e393ed650ef124143f84e9d787fb996e308dd7af6b8f50df72a6f085b67bf9c3 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/os_kv.c | 13 ++++++++++--- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 06fc95c9d8..15f08523a8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sdebugging/testing\sedit\sin\sthe\sprevious\scheck-in. -D 2022-09-12T18:10:41.031 +C Add\sa\sgettimeofday()-based\simplementation\sof\sxCurrentTimeInt64()\sto\sos_kv.c. +D 2022-09-12T19:29:34.236 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -574,7 +574,7 @@ F src/notify.c 89a97dc854c3aa62ad5f384ef50c5a4a11d70fcc69f86de3e991573421130ed6 F src/os.c 0eb831ba3575af5277e47f4edd14fdfc90025c67eb25ce5cda634518d308d4e9 F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 -F src/os_kv.c 226d9051af316dc547a37629b269ba2aebff8fa21915b91a9b41f7872bb4cc05 +F src/os_kv.c 395b3ab831e0d2b172e6dccf6bae3e21b141f3b6d8b3aa828630634edac2604d F src/os_setup.h 0711dbc4678f3ac52d7fe736951b6384a0615387c4ba5135a4764e4e31f4b6a6 F src/os_unix.c d6322b78130d995160bb9cfb7850678ad6838b08c1d13915461b33326a406c04 F src/os_win.c e9454cb141908e8eef2102180bad353a36480612d5b736e4c2bd5777d9b25a34 @@ -2004,8 +2004,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 3354a2edb762d70ccc31d4d25f81b70e644d76e01cb1e81d2e97b5414d809d30 -R 742e0e4f6e4e5630d202911ef2cdc14a +P 6fc8d34c0ae1f8277544be741f2f0835fad8e475d35bd24573224ccc1699b8bd +R d1f30b2b05b4b258ee2d7add5fd2e488 U drh -Z 0a81f46489fd3e0c2c270769139a2009 +Z aaf2d483b1b9208133805d5c75708f65 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bf2f87a85b..c45584142b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6fc8d34c0ae1f8277544be741f2f0835fad8e475d35bd24573224ccc1699b8bd \ No newline at end of file +e393ed650ef124143f84e9d787fb996e308dd7af6b8f50df72a6f085b67bf9c3 \ No newline at end of file diff --git a/src/os_kv.c b/src/os_kv.c index ce0a571965..eebbcd17cd 100644 --- a/src/os_kv.c +++ b/src/os_kv.c @@ -1081,11 +1081,18 @@ static int kvvfsSleep(sqlite3_vfs *pVfs, int nMicro){ ** Return the current time as a Julian Day number in *pTimeOut. */ static int kvvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ - *pTimeOut = 2459829.13362986; - return SQLITE_OK; + sqlite3_int64 i = 0; + int rc; + rc = kvvfsCurrentTimeInt64(0, &i); + *pTimeOut = i/86400000.0; + return rc; } +#include static int kvvfsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ - *pTimeOut = (sqlite3_int64)(2459829.13362986*86400000.0); + static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; + struct timeval sNow; + (void)gettimeofday(&sNow, 0); /* Cannot fail given valid arguments */ + *pTimeOut = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; return SQLITE_OK; } From b126ec18b6f1a6046243073a0ac2dc5f845ed8b1 Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 12 Sep 2022 19:33:20 +0000 Subject: [PATCH 095/428] Fix the <sys/time.h> include in os_kv.c. FossilOrigin-Name: 878cc93e779c3857adc711f212520f007256274fcd3f6f3d72c754a937fcd198 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/os_unix.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 15f08523a8..69204b63ea 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sa\sgettimeofday()-based\simplementation\sof\sxCurrentTimeInt64()\sto\sos_kv.c. -D 2022-09-12T19:29:34.236 +C Fix\sthe\s<sys/time.h>\sinclude\sin\sos_kv.c. +D 2022-09-12T19:33:20.055 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -576,7 +576,7 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 F src/os_kv.c 395b3ab831e0d2b172e6dccf6bae3e21b141f3b6d8b3aa828630634edac2604d F src/os_setup.h 0711dbc4678f3ac52d7fe736951b6384a0615387c4ba5135a4764e4e31f4b6a6 -F src/os_unix.c d6322b78130d995160bb9cfb7850678ad6838b08c1d13915461b33326a406c04 +F src/os_unix.c 0fa91925f0b8831fc0156a9c04d39d86f85baf9eef66c98712395e1715cb75cc F src/os_win.c e9454cb141908e8eef2102180bad353a36480612d5b736e4c2bd5777d9b25a34 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 6176d9752eb580419e8fef4592dc417a6b00ddfd43ee22f818819bf8840ceee8 @@ -2004,8 +2004,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 6fc8d34c0ae1f8277544be741f2f0835fad8e475d35bd24573224ccc1699b8bd -R d1f30b2b05b4b258ee2d7add5fd2e488 +P e393ed650ef124143f84e9d787fb996e308dd7af6b8f50df72a6f085b67bf9c3 +R 7d043d1f24831e04bac4a09dedf809ea U drh -Z aaf2d483b1b9208133805d5c75708f65 +Z f4bc361a8f5540cb46c54b92ebd31edc # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c45584142b..f40ca1a770 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e393ed650ef124143f84e9d787fb996e308dd7af6b8f50df72a6f085b67bf9c3 \ No newline at end of file +878cc93e779c3857adc711f212520f007256274fcd3f6f3d72c754a937fcd198 \ No newline at end of file diff --git a/src/os_unix.c b/src/os_unix.c index 422fb25ed1..784686d0a3 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -93,7 +93,7 @@ #include #include /* amalgamator: keep */ #include -#include +#include /* amalgamator: keep */ #include #if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 # include From 5ca0b38605b5e147b5a8b846c1b6fef826ba362b Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 12 Sep 2022 20:02:33 +0000 Subject: [PATCH 096/428] Fix some problems with handling corrupt database in the recovery extension. FossilOrigin-Name: ed318be8241981ef96334ba13d3201a717cc812a59aed64e3dc67f7e7e71854b --- ext/recover/recoverfault.test | 27 +++++++++++++++++++++------ ext/recover/sqlite3recover.c | 19 +++++++++++++++++-- manifest | 18 +++++++++--------- manifest.uuid | 2 +- src/dbpage.c | 2 +- src/shell.c.in | 2 +- 6 files changed, 50 insertions(+), 20 deletions(-) diff --git a/ext/recover/recoverfault.test b/ext/recover/recoverfault.test index b2411fb6c7..2ac1c67f34 100644 --- a/ext/recover/recoverfault.test +++ b/ext/recover/recoverfault.test @@ -44,19 +44,14 @@ do_execsql_test 1.0 { INSERT INTO t1 VALUES(1, 2, 3); INSERT INTO t1 VALUES(2, hex(randomblob(1000)), randomblob(2000)); CREATE INDEX i1 ON t1(b, c); - CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c); - INSERT INTO t2 VALUES(1, 2, 3); - INSERT INTO t2 VALUES(2, hex(randomblob(1000)), randomblob(2000)); ANALYZE; - PRAGMA writable_schema = 1; - DELETE FROM sqlite_schema WHERE name='t2'; } +faultsim_save_and_close do_faultsim_test 1 -faults oom* -prep { faultsim_restore_and_reopen } -body { set R [sqlite3_recover_init db main test.db2] - $R config lostandfound lost_and_found $R step $R finish } -test { @@ -68,5 +63,25 @@ do_faultsim_test 1 -faults oom* -prep { } } +faultsim_restore_and_reopen +do_execsql_test 2.0 { + CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c); + INSERT INTO t2 VALUES(1, 2, 3); + INSERT INTO t2 VALUES(2, hex(randomblob(1000)), hex(randomblob(2000))); + PRAGMA writable_schema = 1; + DELETE FROM sqlite_schema WHERE name='t2'; +} + +do_faultsim_test 2 -faults oom* -prep { + faultsim_restore_and_reopen +} -body { + set R [sqlite3_recover_init db main test.db2] + $R config lostandfound lost_and_found + $R step + $R finish +} -test { + faultsim_test_result {0 {}} {1 {}} +} + finish_test diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index 849c3bc801..c1ff3b5439 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -406,6 +406,18 @@ static int recoverExec(sqlite3_recover *p, sqlite3 *db, const char *zSql){ return p->errCode; } +static void recoverBindValue( + sqlite3_recover *p, + sqlite3_stmt *pStmt, + int iBind, + sqlite3_value *pVal +){ + if( p->errCode==SQLITE_OK ){ + int rc = sqlite3_bind_value(pStmt, iBind, pVal); + if( rc ) recoverError(p, rc, 0); + } +} + /* ** This function is a no-op if recover handle p already contains an error ** (if p->errCode!=SQLITE_OK). NULL is returned in this case. @@ -1305,7 +1317,7 @@ static void recoverLostAndFoundPopulate( sqlite3_bind_int64(pInsert, 4, iRowid); /* id */ } for(ii=0; iixSql ){ recoverSqlCallback(p, sqlite3_column_text(pInsert, 0)); @@ -1332,6 +1344,9 @@ static void recoverLostAndFoundPopulate( apVal[iField] = sqlite3_value_dup(pVal); assert( iField==nVal || (nVal==-1 && iField==0) ); nVal = iField+1; + if( apVal[iField]==0 ){ + recoverError(p, SQLITE_NOMEM, 0); + } } iPrevRoot = iRoot; @@ -1553,7 +1568,7 @@ static int recoverWriteData(sqlite3_recover *p){ if( pCol->bIPK ){ sqlite3_bind_int64(pInsert, iBind, iRowid); }else if( pCol->iFieldiField]); + recoverBindValue(p, pInsert, iBind, apVal[pCol->iField]); } } } diff --git a/manifest b/manifest index c61b4a13ae..f5b2895b91 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\stests\sfor\sthe\srecover\smodule. -D 2022-09-12T19:23:50.588 +C Fix\ssome\sproblems\swith\shandling\scorrupt\sdatabase\sin\sthe\srecovery\sextension. +D 2022-09-12T20:02:33.923 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -391,10 +391,10 @@ F ext/recover/recover1.test 942016356f9098ca36933536b194b5878827a3a749e0bf41a83d F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c F ext/recover/recoverclobber.test e6537ebf99f57bfff6cca59550b5f4278319b57a89865abb98d755a8fd561d84 F ext/recover/recovercorrupt.test 115cdb67ac29b4e8ec786cee9190ced674f62388f126b20deea22fa5fd11b814 -F ext/recover/recoverfault.test 30e3d1b423b33b4c57f4a97f9754b2d493a411d858b2672ab369d65199bc2420 +F ext/recover/recoverfault.test db6b0ba5a993c38ec885867a12556fa7c5d73948b168b0b59e7d3053c0787f29 F ext/recover/recoverold.test f368a6ae2db12b6017257b332a19ab5df527f4061e43f12f5c85d8e2b236f074 F ext/recover/recoverrowid.test ec4436cd69e6cdacb48dd2963ff6dd9dbd5fe648376de5e7c0c2f4f6cbacb417 -F ext/recover/sqlite3recover.c d40d7c68a118e01d2b06c96325bbe3b85701f3add2ab5aeec289eeafd5e36d6c +F ext/recover/sqlite3recover.c 0154617e73430b594f3d8b94eb2847bd5dc3dc3cd6ff6f0511dc6db5d116621d F ext/recover/sqlite3recover.h 81108efb8c4618d3d9c6da4df785212b0e4501aa0d25edfc463405fe839a6640 F ext/recover/test_recover.c 8f5ef0c9b7523c41a393f65e44d727c23cda8f44d5180fff5b698ee068ba538d F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 @@ -546,7 +546,7 @@ F src/callback.c 4cd7225b26a97f7de5fee5ae10464bed5a78f2adefe19534cc2095b3a8ca484 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c 93e4b5f4faf6d3f688988a116773259a4fbfb4ddac0e9bf9d0ae0429390c2543 F src/date.c 94ce83b4cd848a387680a5f920c9018c16655db778c4d36525af0a0f34679ac5 -F src/dbpage.c 5808e91bc27fa3981b028000f8fadfdc10ce9e59a34ce7dc4e035a69be3906ec +F src/dbpage.c dc1e8e7ba880009986b2f2050213157d2328b6d8cd4df6cf0f48883734510558 F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d F src/delete.c 86573edae75e3d3e9a8b590d87db8e47222103029df4f3e11fa56044459b514e F src/expr.c 24e828db6b2fab8aabfb5d2c0d83dbdfc5a1972b1147fa893350e317ab7e282f @@ -599,7 +599,7 @@ F src/random.c 546d6feb15ec69c1aafe9bb351a277cbb498fd5410e646add673acb805714960 F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c ccce37e7fbe71089cf6aec91e7134c9c0c1d4840cff9f02587bbc71240d914a5 -F src/shell.c.in a805e05f3ed90e0236eb4eb5bdec327bf735d12e754b83ac037cbc22e45ecc09 +F src/shell.c.in 36d5792e35515f2389f175b41486b78b1ad6e571799e3c9b211f47c3629f96e5 F src/sqlite.h.in b9b7fd73239d94db20332bb6e504688001e5564b655e1318a4427a1caef4b99e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h a988810c9b21c0dc36dc7a62735012339dc76fc7ab448fb0792721d30eacb69d @@ -2009,8 +2009,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 1a2540960e40e3c8c622448fd3862e249bd463c29ae4ce5e39942e942533f60a -R 91d8f87cd1493a401d98e4544c1e3f6e +P 37fb093b95c6b7d7ad07a275697df73b69f9fb5c5549aea8544b26e38f24833f +R 4bca180a2c79c76d0affba5c0d731d37 U dan -Z d3a21f62871d8e066c6c2bd48e5f7060 +Z 8bfc7241f27e2d2e9de40f4a070a3e2c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 340320e0cf..f7f9d304f7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -37fb093b95c6b7d7ad07a275697df73b69f9fb5c5549aea8544b26e38f24833f \ No newline at end of file +ed318be8241981ef96334ba13d3201a717cc812a59aed64e3dc67f7e7e71854b \ No newline at end of file diff --git a/src/dbpage.c b/src/dbpage.c index 9b565177c5..7990199fcc 100644 --- a/src/dbpage.c +++ b/src/dbpage.c @@ -288,7 +288,7 @@ static int dbpageColumn( break; } } - return SQLITE_OK; + return rc; } static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ diff --git a/src/shell.c.in b/src/shell.c.in index 632d9633fd..739c2d816c 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -7309,7 +7309,7 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){ pState->db, "main", recoverSqlCb, (void*)pState ); - sqlite3_recover_config(p, SQLITE_RECOVER_TESTDB, (void*)zRecoveryDb); + sqlite3_recover_config(p, 789, (void*)zRecoveryDb); sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF); sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids); sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist); From 5fd8f27bfb5abf924ca26d239b3352d7b6d82264 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 12 Sep 2022 20:21:34 +0000 Subject: [PATCH 097/428] Remove an extraneous key-copy op in the EM_JS impl of kvstorageDelete(). FossilOrigin-Name: 1c5aeee45564c14e7e2e7730f3f52106339ea3148fb5aa786fa767a35db46f51 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/os_kv.c | 1 - 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 69204b63ea..e8ffc8d53e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\s<sys/time.h>\sinclude\sin\sos_kv.c. -D 2022-09-12T19:33:20.055 +C Remove\san\sextraneous\skey-copy\sop\sin\sthe\sEM_JS\simpl\sof\skvstorageDelete(). +D 2022-09-12T20:21:34.667 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -574,7 +574,7 @@ F src/notify.c 89a97dc854c3aa62ad5f384ef50c5a4a11d70fcc69f86de3e991573421130ed6 F src/os.c 0eb831ba3575af5277e47f4edd14fdfc90025c67eb25ce5cda634518d308d4e9 F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 -F src/os_kv.c 395b3ab831e0d2b172e6dccf6bae3e21b141f3b6d8b3aa828630634edac2604d +F src/os_kv.c 889c6f9b59702859f9041549845769bae2cbd6b59cf15bd7904551a676b85533 F src/os_setup.h 0711dbc4678f3ac52d7fe736951b6384a0615387c4ba5135a4764e4e31f4b6a6 F src/os_unix.c 0fa91925f0b8831fc0156a9c04d39d86f85baf9eef66c98712395e1715cb75cc F src/os_win.c e9454cb141908e8eef2102180bad353a36480612d5b736e4c2bd5777d9b25a34 @@ -2004,8 +2004,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e393ed650ef124143f84e9d787fb996e308dd7af6b8f50df72a6f085b67bf9c3 -R 7d043d1f24831e04bac4a09dedf809ea -U drh -Z f4bc361a8f5540cb46c54b92ebd31edc +P 878cc93e779c3857adc711f212520f007256274fcd3f6f3d72c754a937fcd198 +R ea387427fef090c92e0fdcd9a6ab25a2 +U stephan +Z d8eaf9989333b333239c3719fa4ba2d7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f40ca1a770..8dbaf37dda 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -878cc93e779c3857adc711f212520f007256274fcd3f6f3d72c754a937fcd198 \ No newline at end of file +1c5aeee45564c14e7e2e7730f3f52106339ea3148fb5aa786fa767a35db46f51 \ No newline at end of file diff --git a/src/os_kv.c b/src/os_kv.c index eebbcd17cd..62214a13ad 100644 --- a/src/os_kv.c +++ b/src/os_kv.c @@ -284,7 +284,6 @@ EM_JS(int, kvstorageDelete, try { const zXKey = kvstorageMakeKeyOnJSStack(zClass,zKey); if(!zXKey) return 1/*OOM*/; - _sqlite3_wasm__kvvfsMakeKey(zClass, zKey, zXKey); const jKey = UTF8ToString(zXKey); ((115/*=='s'*/===getValue(zClass)) ? sessionStorage : localStorage).removeItem(jKey); From 23d39ce54d2001af90e453765a38adcc964e5820 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 12 Sep 2022 22:27:00 +0000 Subject: [PATCH 098/428] Get testing1.js working with a kvvfs build. FossilOrigin-Name: 333a45725d1708e0fefa559c33ce1c7eeb425cdb04f594b1f2b48166c1478c79 --- ext/wasm/GNUmakefile | 5 +++-- ext/wasm/kvvfs.make | 20 +++++++++++++++----- ext/wasm/testing1.html | 2 ++ ext/wasm/testing1.js | 17 +++++++++++++---- manifest | 18 +++++++++--------- manifest.uuid | 2 +- 6 files changed, 43 insertions(+), 21 deletions(-) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 0cf365b912..ff37281ea4 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -316,10 +316,11 @@ dir.sql := sql speedtest1 := ../../speedtest1 speedtest1.c := ../../test/speedtest1.c speedtest1.sql := $(dir.sql)/speedtest1.sql +speedtest1.cliflags := --size 100 $(speedtest1): $(MAKE) -C ../.. speedtest1 -$(speedtest1.sql): $(speedtest1) - $(speedtest1) --script $@ +$(speedtest1.sql): $(speedtest1) $(MAKEFILE) + $(speedtest1) $(speedtest1.cliflags) --script $@ batch-runner.list: $(MAKEFILE) $(speedtest1.sql) $(dir.sql)/000-mandelbrot.sql bash split-speedtest1-script.sh $(dir.sql)/speedtest1.sql ls -1 $(dir.sql)/*.sql | grep -v speedtest1.sql | sort > $@ diff --git a/ext/wasm/kvvfs.make b/ext/wasm/kvvfs.make index cfe0af652c..ad743dfda6 100644 --- a/ext/wasm/kvvfs.make +++ b/ext/wasm/kvvfs.make @@ -34,6 +34,12 @@ kvvfs.cflags += -std=c99 -fPIC -g kvvfs.cflags += -I. -I$(dir.top) kvvfs.cflags += -DSQLITE_OS_KV=1 $(SQLITE_OPT) +kvvfs.extra.c := +ifeq (1,1) + # To get testing1.js to run with $(kvvfs.js) we need... + kvvfs.extra.c += $(jaccwabyt_test.c) +endif + ######################################################################## # emcc flags specific to building the final .js/.wasm file... kvvfs.jsflags := -fPIC @@ -44,7 +50,12 @@ kvvfs.jsflags += -sMODULARIZE kvvfs.jsflags += -sSTRICT_JS kvvfs.jsflags += -sDYNAMIC_EXECUTION=0 kvvfs.jsflags += -sNO_POLYFILL -kvvfs.jsflags += -sEXPORTED_FUNCTIONS=@$(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api +ifeq (,$(kvvfs.extra.c)) + kvvfs.jsflags += -sEXPORTED_FUNCTIONS=@$(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api +else + # need more exports for jaccwabyt test code... + kvvfs.jsflags += -sEXPORTED_FUNCTIONS=@$(dir.wasm)/EXPORTED_FUNCTIONS.api +endif kvvfs.jsflags += -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory,allocateUTF8OnStack # wasmMemory ==> for -sIMPORTED_MEMORY # allocateUTF8OnStack ==> kvvfs internals @@ -69,13 +80,12 @@ kvvfs.jsflags += -sMEMORY64=0 ifneq (0,$(enable_bigint)) kvvfs.jsflags += -sWASM_BIGINT endif - -$(kvvfs.js): $(MAKEFILE) $(MAKEFILE.kvvfs) $(kvvfs.wasm.c) $(sqlite3.c) \ - EXPORTED_FUNCTIONS.api \ +$(kvvfs.js): $(kvvfs.wasm.c) $(sqlite3.c) $(kvvfs.extra.c) \ + EXPORTED_FUNCTIONS.api $(MAKEFILE) $(MAKEFILE.kvvfs) \ $(post-js.js) $(emcc.bin) -o $@ $(emcc_opt) $(emcc.flags) \ $(SQLITE_OPT) \ - $(kvvfs.cflags) $(kvvfs.jsflags) $(kvvfs.wasm.c) + $(kvvfs.cflags) $(kvvfs.jsflags) $(kvvfs.wasm.c) $(kvvfs.extra.c) chmod -x $(kvvfs.wasm) ifneq (,$(wasm-strip)) $(wasm-strip) $(kvvfs.wasm) diff --git a/ext/wasm/testing1.html b/ext/wasm/testing1.html index 24c1e82360..bf73974ee7 100644 --- a/ext/wasm/testing1.html +++ b/ext/wasm/testing1.html @@ -27,6 +27,8 @@
Most stuff on this page happens in the dev console.

+ + diff --git a/ext/wasm/testing1.js b/ext/wasm/testing1.js index 4144219527..2aadc30c3a 100644 --- a/ext/wasm/testing1.js +++ b/ext/wasm/testing1.js @@ -252,7 +252,7 @@ db.exec({ sql:new TextEncoder('utf-8').encode([ // ^^^ testing string-vs-typedarray handling in execMulti() - "attach 'foo.db' as foo;", + "attach 'session' as foo;" /* name 'session' is magic for kvvfs! */, "create table foo.bar(a);", "insert into foo.bar(a) values(1),(2),(3);", "select a from foo.bar order by a;" @@ -744,7 +744,7 @@ .assert('sqlite3_vfs' === dVfs.structName) .assert(!!dVfs.structInfo) .assert(SB.StructType.hasExternalPointer(dVfs)) - .assert(3===dVfs.$iVersion) + .assert(dVfs.$iVersion>0) .assert('number'===typeof dVfs.$zName) .assert('number'===typeof dVfs.$xSleep) .assert(capi.wasm.functionEntry(dVfs.$xOpen)) @@ -1046,9 +1046,18 @@ T.assert(capi.wasm[k] instanceof Function); }); - const db = new oo.DB(':memory:'), startTime = performance.now(); + let dbName = "/testing1.sqlite3"; + let vfsName = undefined; + if(oo.DB.clearKvvfsStorage){ + dbName = "local"; + vfsName = 'kvvfs'; + logHtml("Found kvvfs. Clearing db(s) from sessionStorage and localStorage", + "and selecting kvvfs-friendly db name:",dbName); + oo.DB.clearKvvfsStorage(); + } + const db = new oo.DB(dbName,'c',vfsName), startTime = performance.now(); try { - log("DB filename:",db.filename,db.fileName()); + log("db.filename =",db.filename,"db.fileName() =",db.fileName()); const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'; [ diff --git a/manifest b/manifest index 75b3e09950..b104be806b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\skv-vfs\sbranch\sinto\sfiddle-opfs\sand\sadd\sspeedtest1-kvvfs.html\stest\sapp\s(which\scurrently\sfails\searly\son). -D 2022-09-12T20:18:28.723 +C Get\stesting1.js\sworking\swith\sa\skvvfs\sbuild. +D 2022-09-12T22:27:00.037 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 0434268aa7e042ec265c13e85a82cc3a760be02851ba428aa996a36d4ba77dfd +F ext/wasm/GNUmakefile b175a00599c976fe9e7bc02bbc1337b5e3b81d042320c3a0be1621d2c7a21b21 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 1dfd067b3cbd9a49cb204097367cf2f8fe71b5a3b245d9d82a24779fd4ac2394 F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -506,7 +506,7 @@ F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab26 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19 -F ext/wasm/kvvfs.make 1858354b02ebdccc459fb2d235552f8951d6ce28340083da9bb230bcf2533789 +F ext/wasm/kvvfs.make bfa0aaac384d9f200d2c8e31efb3536b40d139667b88e6eba9c0a71e23da6a5c F ext/wasm/kvvfs1.html 83bac238d1e93ed102a461672fc58fe74e611e426708e9a5c66002c01eadb942 F ext/wasm/kvvfs1.js 53721a42e0ec45f6978cc723e5de47f882517867d0fcff4c6ff05f4c422e27c4 F ext/wasm/scratchpad-opfs-main.html 4565cf194e66188190d35f70e82553e2e2d72b9809b73c94ab67b8cfd14d2e0c @@ -525,8 +525,8 @@ F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 F ext/wasm/testing-worker1-promiser.js c62b5879339eef0b21aebd9d75bc125c86530edc17470afff18077f931cb704a -F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409 -F ext/wasm/testing1.js 2def7a86c52ff28b145cb86188d5c7a49d5993f9b78c50d140e1c31551220955 +F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae +F ext/wasm/testing1.js 735120231d6e55b92964924fcb54a66484d8b2e30fe5a18c71081b3590344a5f F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x @@ -2024,8 +2024,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d845c6c22bd5d3fffc66e0566df346d690dd8bd1fc1688e312161b1a1edcfd79 878cc93e779c3857adc711f212520f007256274fcd3f6f3d72c754a937fcd198 -R af6be1e15f44117c1188e0c623230580 +P 8b1608a9a37131121cb3a0cb1e8dbad5e39dc1fc2a341d056f09537f179b2e7a +R 77095a41dfb9b784dcab0078822e6eff U stephan -Z 1b00f3836ac4e8ae817bece731c1dd12 +Z c624cb9efadffe8972e3df15b8dc9939 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e78c4a139f..69b3e88fd2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8b1608a9a37131121cb3a0cb1e8dbad5e39dc1fc2a341d056f09537f179b2e7a \ No newline at end of file +333a45725d1708e0fefa559c33ce1c7eeb425cdb04f594b1f2b48166c1478c79 \ No newline at end of file From 7985e05a9729fa44a4c0969caa694825d56da1c7 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 13 Sep 2022 18:08:24 +0000 Subject: [PATCH 099/428] Add tests. Deal issues surrounding aborting recovery from within the SQL callback, and avoiding the pending-byte page. FossilOrigin-Name: 4e97dd31f5240d9231167ae172a5116426c42177a1ed3c5422b9d51b762d5a87 --- ext/recover/recover1.test | 5 +-- ext/recover/recoverclobber.test | 2 +- ext/recover/recovercorrupt.test | 2 +- ext/recover/recoverfault.test | 4 +-- ext/recover/recoverold.test | 8 +++-- ext/recover/recoverrowid.test | 2 +- ext/recover/recoversql.test | 60 +++++++++++++++++++++++++++++++++ ext/recover/sqlite3recover.c | 10 ++++-- ext/recover/test_recover.c | 16 ++++++--- manifest | 29 ++++++++-------- manifest.uuid | 2 +- src/dbpage.c | 16 ++++++--- 12 files changed, 120 insertions(+), 36 deletions(-) create mode 100644 ext/recover/recoversql.test diff --git a/ext/recover/recover1.test b/ext/recover/recover1.test index 5f1bb2b0fe..3ee4c18ad6 100644 --- a/ext/recover/recover1.test +++ b/ext/recover/recover1.test @@ -45,7 +45,7 @@ proc do_recover_test {tn} { uplevel [list do_test $tn.1 { set R [sqlite3_recover_init db main test.db2] $R config testdb rstate.db - $R step + $R run $R finish } {}] @@ -61,7 +61,7 @@ proc do_recover_test {tn} { set R [sqlite3_recover_init_sql db main my_sql_hook] $R config testdb rstate.db $R config rowids 1 - $R step + $R run $R finish } {}] @@ -75,6 +75,7 @@ proc do_recover_test {tn} { proc my_sql_hook {sql} { lappend ::sqlhook $sql + return 0 } do_execsql_test 1.0 { diff --git a/ext/recover/recoverclobber.test b/ext/recover/recoverclobber.test index e6967ec9be..537af8e7c1 100644 --- a/ext/recover/recoverclobber.test +++ b/ext/recover/recoverclobber.test @@ -25,7 +25,7 @@ ifcapable !vtab { proc recover {db output} { set R [sqlite3_recover_init db main test.db2] - $R step + $R run $R finish } diff --git a/ext/recover/recovercorrupt.test b/ext/recover/recovercorrupt.test index ff1d2af19f..54a94e1b0c 100644 --- a/ext/recover/recovercorrupt.test +++ b/ext/recover/recovercorrupt.test @@ -58,7 +58,7 @@ for {set ii 200} {$ii < 10000} {incr ii} { do_test 1.2.$ii { set R [sqlite3_recover_init db main test.db2] $R config lostandfound lost_and_found - $R step + $R run $R finish } {} } diff --git a/ext/recover/recoverfault.test b/ext/recover/recoverfault.test index 2ac1c67f34..0241034100 100644 --- a/ext/recover/recoverfault.test +++ b/ext/recover/recoverfault.test @@ -52,7 +52,7 @@ do_faultsim_test 1 -faults oom* -prep { faultsim_restore_and_reopen } -body { set R [sqlite3_recover_init db main test.db2] - $R step + $R run $R finish } -test { faultsim_test_result {0 {}} {1 {}} @@ -77,7 +77,7 @@ do_faultsim_test 2 -faults oom* -prep { } -body { set R [sqlite3_recover_init db main test.db2] $R config lostandfound lost_and_found - $R step + $R run $R finish } -test { faultsim_test_result {0 {}} {1 {}} diff --git a/ext/recover/recoverold.test b/ext/recover/recoverold.test index 83f210aebc..691737bdc5 100644 --- a/ext/recover/recoverold.test +++ b/ext/recover/recoverold.test @@ -47,7 +47,7 @@ proc do_recover_test {tn {tsql {}} {res {}}} { set R [sqlite3_recover_init db main test.db2] $R config lostandfound lost_and_found - $R step + $R run $R finish sqlite3 db2 test.db2 @@ -65,7 +65,7 @@ proc do_recover_test {tn {tsql {}} {res {}}} { set ::sqlhook [list] set R [sqlite3_recover_init_sql db main my_sql_hook] $R config lostandfound lost_and_found - $R step + $R run $R finish sqlite3 db2 test.db2 @@ -81,6 +81,7 @@ proc do_recover_test {tn {tsql {}} {res {}}} { proc my_sql_hook {sql} { lappend ::sqlhook $sql + return 0 } @@ -154,6 +155,7 @@ do_recover_test 2.4.1 { 2 2 3 {} 8 9 7 } +breakpoint do_execsql_test 2.5 { CREATE TABLE x1(a, b, c); WITH s(i) AS ( @@ -176,7 +178,7 @@ do_test 2.6 { set R [sqlite3_recover_init db main test.db2] $R config lostandfound lost_and_found $R config freelistcorrupt 1 - $R step + $R run $R finish sqlite3 db2 test.db2 execsql { SELECT count(*) FROM lost_and_found_1; } db2 diff --git a/ext/recover/recoverrowid.test b/ext/recover/recoverrowid.test index 3dfafbcc7f..bd47422eaf 100644 --- a/ext/recover/recoverrowid.test +++ b/ext/recover/recoverrowid.test @@ -28,7 +28,7 @@ proc recover {db bRowids output} { set R [sqlite3_recover_init db main test.db2] $R config rowids $bRowids - $R step + $R run $R finish } diff --git a/ext/recover/recoversql.test b/ext/recover/recoversql.test new file mode 100644 index 0000000000..19f7ec7a1c --- /dev/null +++ b/ext/recover/recoversql.test @@ -0,0 +1,60 @@ +# 2022 September 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. +# +#*********************************************************************** +# +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] recover_common.tcl] +source $testdir/tester.tcl +set testprefix recoversql + +ifcapable !vtab { + finish_test; return +} + +do_execsql_test 1.0 { + CREATE TABLE "x.1" (x, y); + INSERT INTO "x.1" VALUES(1, 1), (2, 2), (3, 3); + CREATE INDEX "i.1" ON "x.1"(y, x); +} + +proc sql_hook {sql} { + incr ::iSqlHook + if {$::iSqlHook==$::sql_hook_cnt} { return 4 } + return 0 +} + +do_test 1.1 { + set ::sql_hook_cnt -1 + set ::iSqlHook 0 + set R [sqlite3_recover_init_sql db main sql_hook] + $R run + $R finish +} {} + +set nSqlCall $iSqlHook + +for {set ii 1} {$ii<$nSqlCall} {incr ii} { + set iSqlHook 0 + set sql_hook_cnt $ii + do_test 1.$ii.a { + set R [sqlite3_recover_init_sql db main sql_hook] + $R run + } {1} + do_test 1.$ii.b { + list [catch { $R finish } msg] $msg + } {1 {callback returned an error - 4}} +} + + +finish_test diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index c1ff3b5439..206a6fad5b 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -406,6 +406,11 @@ static int recoverExec(sqlite3_recover *p, sqlite3 *db, const char *zSql){ return p->errCode; } +/* +** Bind the value pVal to parameter iBind of statement pStmt. Leave an +** error in the recover handle passed as the first argument if an error +** (e.g. an OOM) occurs. +*/ static void recoverBindValue( sqlite3_recover *p, sqlite3_stmt *pStmt, @@ -559,7 +564,7 @@ static void recoverGetPage( return; }else{ if( p->pGetPage==0 ){ - pStmt = recoverPreparePrintf( + pStmt = p->pGetPage = recoverPreparePrintf( p, p->dbIn, "SELECT data FROM sqlite_dbpage(%Q) WHERE pgno=?", p->zDb ); }else{ @@ -567,11 +572,12 @@ static void recoverGetPage( } if( pStmt ){ + int rc = SQLITE_OK; sqlite3_bind_int64(pStmt, 1, pgno); if( SQLITE_ROW==sqlite3_step(pStmt) ){ sqlite3_result_value(pCtx, sqlite3_column_value(pStmt, 0)); } - p->pGetPage = recoverReset(p, pStmt); + recoverReset(p, pStmt); } } diff --git a/ext/recover/test_recover.c b/ext/recover/test_recover.c index a7c5afd3aa..b273fbcd1d 100644 --- a/ext/recover/test_recover.c +++ b/ext/recover/test_recover.c @@ -42,8 +42,16 @@ static int xSqlCallback(void *pSqlArg, const char *zSql){ if( res ){ Tcl_BackgroundError(p->interp); return TCL_ERROR; + }else{ + Tcl_Obj *pObj = Tcl_GetObjResult(p->interp); + if( Tcl_GetCharLength(pObj)==0 ){ + res = 0; + }else if( Tcl_GetIntFromObj(p->interp, pObj, &res) ){ + Tcl_BackgroundError(p->interp); + return TCL_ERROR; + } } - return SQLITE_OK; + return res; } static int getDbPointer(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **pDb){ @@ -60,7 +68,7 @@ static int getDbPointer(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **pDb){ ** Implementation of the command created by [sqlite3_recover_init]: ** ** $cmd config OP ARG -** $cmd step +** $cmd run ** $cmd errmsg ** $cmd errcode ** $cmd finalize @@ -77,7 +85,7 @@ static int testRecoverCmd( const char *zMsg; } aSub[] = { { "config", 2, "REBASE-BLOB" }, /* 0 */ - { "step", 0, "" }, /* 1 */ + { "run", 0, "" }, /* 1 */ { "errmsg", 0, "" }, /* 2 */ { "errcode", 0, "" }, /* 3 */ { "finish", 0, "" }, /* 4 */ @@ -145,7 +153,7 @@ static int testRecoverCmd( Tcl_SetObjResult(interp, Tcl_NewIntObj(res)); break; } - case 1: assert( sqlite3_stricmp("step", aSub[iSub].zSub)==0 ); { + case 1: assert( sqlite3_stricmp("run", aSub[iSub].zSub)==0 ); { int res = sqlite3_recover_run(pTest->p); Tcl_SetObjResult(interp, Tcl_NewIntObj(res)); break; diff --git a/manifest b/manifest index f5b2895b91..3f6d9ab674 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\ssome\sproblems\swith\shandling\scorrupt\sdatabase\sin\sthe\srecovery\sextension. -D 2022-09-12T20:02:33.923 +C Add\stests.\sDeal\sissues\ssurrounding\saborting\srecovery\sfrom\swithin\sthe\sSQL\scallback,\sand\savoiding\sthe\spending-byte\spage. +D 2022-09-13T18:08:24.770 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -387,16 +387,17 @@ F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2 F ext/rbu/sqlite3rbu.c 8737cabdfbee84bb25a7851ecef8b1312be332761238da9be6ddb10c62ad4291 F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812 F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a -F ext/recover/recover1.test 942016356f9098ca36933536b194b5878827a3a749e0bf41a83d83530c0d0ea8 +F ext/recover/recover1.test d0fa2f945aac4754e0abb802941b9b80efd4828a775a090f9d2253f5a0ee1d0e F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c -F ext/recover/recoverclobber.test e6537ebf99f57bfff6cca59550b5f4278319b57a89865abb98d755a8fd561d84 -F ext/recover/recovercorrupt.test 115cdb67ac29b4e8ec786cee9190ced674f62388f126b20deea22fa5fd11b814 -F ext/recover/recoverfault.test db6b0ba5a993c38ec885867a12556fa7c5d73948b168b0b59e7d3053c0787f29 -F ext/recover/recoverold.test f368a6ae2db12b6017257b332a19ab5df527f4061e43f12f5c85d8e2b236f074 -F ext/recover/recoverrowid.test ec4436cd69e6cdacb48dd2963ff6dd9dbd5fe648376de5e7c0c2f4f6cbacb417 -F ext/recover/sqlite3recover.c 0154617e73430b594f3d8b94eb2847bd5dc3dc3cd6ff6f0511dc6db5d116621d +F ext/recover/recoverclobber.test 294dcc894124ab4ca3a7b35766630742a3d25810fceac22220beb64f70a33a60 +F ext/recover/recovercorrupt.test e3f3cbe0162ba681518aac9ea0ae8119f32ac93fb0900b5f09b6318966108e54 +F ext/recover/recoverfault.test 862ab02bd503922281a5ee9f81a3a92312561bb7a39e2fdb06e6afd60c3a167e +F ext/recover/recoverold.test 46e9d99b595fac583d4c67f74d7d89c20a435c752ef6eeb3e918b599940c88e0 +F ext/recover/recoverrowid.test 1694a1a5526d825f71279f3d02ab02a1ee4c5265de18858bf54cb8ec54487ac8 +F ext/recover/recoversql.test f9872ff2114e13ffd8ee31e1de06919f62b9b48bc080191b5bd076d10becb60f +F ext/recover/sqlite3recover.c ed5ab827433823ebf3ad7ada192ecefdc8ef9327af68fd4db9ae463a11bbc476 F ext/recover/sqlite3recover.h 81108efb8c4618d3d9c6da4df785212b0e4501aa0d25edfc463405fe839a6640 -F ext/recover/test_recover.c 8f5ef0c9b7523c41a393f65e44d727c23cda8f44d5180fff5b698ee068ba538d +F ext/recover/test_recover.c 5941ecf484b6158be26e34c6f7b6c7f03967c72a63db0c08e7fd0fa43023eafa F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 F ext/repair/checkfreelist.c e21f06995ff4efdc1622dcceaea4dcba2caa83ca2f31a1607b98a8509168a996 F ext/repair/checkindex.c 4383e4469c21e5b9ae321d0d63cec53e981af9d7a6564be6374f0eeb93dfc890 @@ -546,7 +547,7 @@ F src/callback.c 4cd7225b26a97f7de5fee5ae10464bed5a78f2adefe19534cc2095b3a8ca484 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c 93e4b5f4faf6d3f688988a116773259a4fbfb4ddac0e9bf9d0ae0429390c2543 F src/date.c 94ce83b4cd848a387680a5f920c9018c16655db778c4d36525af0a0f34679ac5 -F src/dbpage.c dc1e8e7ba880009986b2f2050213157d2328b6d8cd4df6cf0f48883734510558 +F src/dbpage.c 433f13b34f1e5a84cbb5f10214c12471c6f0197f276389634a1cc393ec821c3d F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d F src/delete.c 86573edae75e3d3e9a8b590d87db8e47222103029df4f3e11fa56044459b514e F src/expr.c 24e828db6b2fab8aabfb5d2c0d83dbdfc5a1972b1147fa893350e317ab7e282f @@ -2009,8 +2010,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 37fb093b95c6b7d7ad07a275697df73b69f9fb5c5549aea8544b26e38f24833f -R 4bca180a2c79c76d0affba5c0d731d37 +P ed318be8241981ef96334ba13d3201a717cc812a59aed64e3dc67f7e7e71854b +R 11a8fecaa81e021bbb20605615a33134 U dan -Z 8bfc7241f27e2d2e9de40f4a070a3e2c +Z 28892e8b77cb290f00759179724671d9 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f7f9d304f7..9694fdd0c2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ed318be8241981ef96334ba13d3201a717cc812a59aed64e3dc67f7e7e71854b \ No newline at end of file +4e97dd31f5240d9231167ae172a5116426c42177a1ed3c5422b9d51b762d5a87 \ No newline at end of file diff --git a/src/dbpage.c b/src/dbpage.c index 7990199fcc..e13740d81d 100644 --- a/src/dbpage.c +++ b/src/dbpage.c @@ -274,12 +274,18 @@ static int dbpageColumn( } case 1: { /* data */ DbPage *pDbPage = 0; - rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0); - if( rc==SQLITE_OK ){ - sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage, - SQLITE_TRANSIENT); + if( pCsr->pgno==((PENDING_BYTE/pCsr->szPage)+1) ){ + /* The pending byte page. Assume it is zeroed out. Attempting to + ** request this page from the page is an SQLITE_CORRUPT error. */ + sqlite3_result_zeroblob(ctx, pCsr->szPage); + }else{ + rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0); + if( rc==SQLITE_OK ){ + sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage, + SQLITE_TRANSIENT); + } + sqlite3PagerUnref(pDbPage); } - sqlite3PagerUnref(pDbPage); break; } default: { /* schema */ From 5b9150079606c31741572ef2aca5e39e8f7f9b56 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 13 Sep 2022 19:27:03 +0000 Subject: [PATCH 100/428] Add/apply various kvvfs-specific utility APIs to the JS layer to assist in testing and analysis. Correct a backwards default arg check for sqlite3ApiBootstrap(). Add exports for sqlite3_db_handle(), sqlite3_file_control(), and the SQLITE_FCNTL_xxx enum. FossilOrigin-Name: 0d78961870ee9f22f1ba16d423377d28dcc36e04b1e31ffd57f3e2fd51f8f0f2 --- ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api | 2 + ext/wasm/api/sqlite3-api-cleanup.js | 27 ++- ext/wasm/api/sqlite3-api-glue.js | 3 +- ext/wasm/api/sqlite3-api-oo1.js | 68 +++---- ext/wasm/api/sqlite3-api-prologue.js | 185 ++++++++++++++++---- ext/wasm/api/sqlite3-wasm.c | 48 ++++- ext/wasm/batch-runner.html | 4 +- ext/wasm/batch-runner.js | 51 ++++-- ext/wasm/kvvfs1.html | 1 + ext/wasm/kvvfs1.js | 24 +-- ext/wasm/testing1.js | 19 +- manifest | 32 ++-- manifest.uuid | 2 +- 13 files changed, 340 insertions(+), 126 deletions(-) diff --git a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api index aead79e50d..f03478b17c 100644 --- a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api +++ b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api @@ -25,6 +25,7 @@ _sqlite3_compileoption_used _sqlite3_create_function_v2 _sqlite3_data_count _sqlite3_db_filename +_sqlite3_db_handle _sqlite3_db_name _sqlite3_errmsg _sqlite3_error_offset @@ -33,6 +34,7 @@ _sqlite3_exec _sqlite3_expanded_sql _sqlite3_extended_errcode _sqlite3_extended_result_codes +_sqlite3_file_control _sqlite3_finalize _sqlite3_initialize _sqlite3_interrupt diff --git a/ext/wasm/api/sqlite3-api-cleanup.js b/ext/wasm/api/sqlite3-api-cleanup.js index 1b57cdc5de..0e99edf508 100644 --- a/ext/wasm/api/sqlite3-api-cleanup.js +++ b/ext/wasm/api/sqlite3-api-cleanup.js @@ -20,11 +20,17 @@ if('undefined' !== typeof Module){ // presumably an Emscripten build /** Install a suitable default configuration for sqlite3ApiBootstrap(). */ - const SABC = self.sqlite3ApiBootstrap.defaultConfig; - SABC.Module = Module /* ==> Currently needs to be exposed here for test code. NOT part - of the public API. */; - SABC.exports = Module['asm']; - SABC.memory = Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */; + const SABC = self.sqlite3ApiConfig || Object.create(null); + if(undefined===SABC.Module){ + SABC.Module = Module /* ==> Currently needs to be exposed here for + test code. NOT part of the public API. */; + } + if(undefined===SABC.exports){ + SABC.exports = Module['asm']; + } + if(undefined===SABC.memory){ + SABC.memory = Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */; + } /** For current (2022-08-22) purposes, automatically call @@ -35,8 +41,15 @@ if('undefined' !== typeof Module){ // presumably an Emscripten build configuration used by a no-args call to sqlite3ApiBootstrap(). */ //console.warn("self.sqlite3ApiConfig = ",self.sqlite3ApiConfig); - const sqlite3 = self.sqlite3ApiBootstrap(); - delete self.sqlite3ApiBootstrap; + const rmApiConfig = (SABC !== self.sqlite3ApiConfig); + self.sqlite3ApiConfig = SABC; + let sqlite3; + try{ + sqlite3 = self.sqlite3ApiBootstrap(); + }finally{ + delete self.sqlite3ApiBootstrap; + if(rmApiConfig) delete self.sqlite3ApiConfig; + } if(self.location && +self.location.port > 1024){ console.warn("Installing sqlite3 bits as global S for local dev/test purposes."); diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index 3a9e8803cb..67f9403548 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -55,6 +55,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ */ const aPtr = wasm.xWrap.argAdapter('*'); wasm.xWrap.argAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr); + wasm.xWrap.resultAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr); /** Populate api object with sqlite3_...() by binding the "raw" wasm @@ -174,7 +175,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ wasm.ctype = JSON.parse(wasm.cstringToJs(cJson)); //console.debug('wasm.ctype length =',wasm.cstrlen(cJson)); for(const t of ['access', 'blobFinalizers', 'dataTypes', - 'encodings', 'flock', 'ioCap', + 'encodings', 'fcntl', 'flock', 'ioCap', 'openFlags', 'prepareFlags', 'resultCodes', 'syncFlags', 'udfFlags', 'version' ]){ diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 3dfe5bfb05..8280204d62 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -496,6 +496,13 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ the statement actually produces any result rows. ================================================================== + - `.columnNames`: if this is an array, the column names of the + result set are stored in this array before the callback (if + any) is triggered (regardless of whether the query produces any + result rows). If no statement has result columns, this value is + unchanged. Achtung: an SQL result may have multiple columns + with identical names. + - `.callback` = a function which gets called for each row of the result set, but only if that statement has any result _rows_. The callback's "this" is the options object. The second @@ -523,8 +530,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ as the first argument to the callback: A.1) `'array'` (the default) causes the results of - `stmt.get([])` to be passed to passed on and/or appended to - `resultRows`. + `stmt.get([])` to be passed to the `callback` and/or appended + to `resultRows`. A.2) `'object'` causes the results of `stmt.get(Object.create(null))` to be passed to the @@ -536,7 +543,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ A.3) `'stmt'` causes the current Stmt to be passed to the callback, but this mode will trigger an exception if `resultRows` is an array because appending the statement to - the array would be unhelpful. + the array would be downright unhelpful. B) An integer, indicating a zero-based column in the result row. Only that one single value will be passed on. @@ -545,10 +552,14 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ':', '$', or '@' will fetch the row as an object, extract that one field, and pass that field's value to the callback. Note that these keys are case-sensitive so must match the case used - in the SQL. e.g. `"select a A from t"` with a `rowMode` of '$A' - would work but '$a' would not. A reference to a column not in - the result set will trigger an exception on the first row (as - the check is not performed until rows are fetched). + in the SQL. e.g. `"select a A from t"` with a `rowMode` of + `'$A'` would work but `'$a'` would not. A reference to a column + not in the result set will trigger an exception on the first + row (as the check is not performed until rows are fetched). + Note also that `$` is a legal identifier character in JS so + need not be quoted. (Design note: those 3 characters were + chosen because they are the characters support for naming bound + parameters.) Any other `rowMode` value triggers an exception. @@ -560,12 +571,17 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ and can be used over a WebWorker-style message interface. exec() throws if `resultRows` is set and `rowMode` is 'stmt'. - - `.columnNames`: if this is an array, the column names of the - result set are stored in this array before the callback (if - any) is triggered (regardless of whether the query produces any - result rows). If no statement has result columns, this value is - unchanged. Achtung: an SQL result may have multiple columns - with identical names. + + Potential TODOs: + + - `.bind`: permit an array of arrays/objects to bind. The first + sub-array would act on the first statement which has bindable + parameters (as it does now). The 2nd would act on the next such + statement, etc. + + - `.callback` and `.resultRows`: permit an array entries with + semantics similar to those described for `.bind` above. + */ exec: function(/*(sql [,obj]) || (obj)*/){ affirmDbOpen(this); @@ -1580,31 +1596,5 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ Stmt }/*oo1 object*/; - if( self.window===self && 0!==capi.sqlite3_vfs_find('kvvfs') ){ - /* Features specific to kvvfs... */ - /** - Clears all storage used by the kvvfs DB backend, deleting any - DB(s) stored there. Its argument must be either 'session', - 'local', or ''. In the first two cases, only sessionStorage - resp. localStorage is cleared. If it's an empty string (the - default) then both are cleared. Only storage keys which match - the pattern used by kvvfs are cleared: any other client-side - data are retained. - */ - DB.clearKvvfsStorage = function(which=''){ - const prefix = 'kvvfs-'+which; - const stores = []; - if('session'===which || ''===which) stores.push(sessionStorage); - if('local'===which || ''===which) stores.push(localStorage); - stores.forEach(function(s){ - const toRm = []; - let i = 0, k; - for( i = 0; (k = s.key(i)); ++i ){ - if(k.startsWith(prefix)) toRm.push(k); - } - toRm.forEach((kk)=>s.removeItem(kk)); - }); - }; - }/* main-window-only bits */ }); diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 17dcd42289..1c22e9ea21 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -43,10 +43,7 @@ - Insofar as possible, support client-side storage using JS filesystem APIs. As of this writing, such things are still very - much TODO. Initial testing with using IndexedDB as backing storage - showed it to work reasonably well, but it's also too easy to - corrupt by using a web page in two browser tabs because IndexedDB - lacks the locking features needed to support that. + much under development. Specific non-goals of this project: @@ -54,8 +51,10 @@ Encodings in that realm, there are no currently plans to support the UTF16-related sqlite3 APIs. They would add a complication to the bindings for no appreciable benefit. Though web-related - implementation details take priority, the lower-level WASM module - "should" work in non-web WASM environments. + implementation details take priority, and the JavaScript + components of the API specifically focus on browser clients, the + lower-level WASM module "should" work in non-web WASM + environments. - Supporting old or niche-market platforms. WASM is built for a modern web and requires modern platforms. @@ -78,17 +77,18 @@ */ /** - sqlite3ApiBootstrap() is the only global symbol exposed by this - API. It is intended to be called one time at the end of the API - amalgamation process, passed configuration details for the current - environment, and then optionally be removed from the global object - using `delete self.sqlite3ApiBootstrap`. + sqlite3ApiBootstrap() is the only global symbol persistently + exposed by this API. It is intended to be called one time at the + end of the API amalgamation process, passed configuration details + for the current environment, and then optionally be removed from + the global object using `delete self.sqlite3ApiBootstrap`. This function expects a configuration object, intended to abstract away details specific to any given WASM environment, primarily so that it can be used without any _direct_ dependency on - Emscripten. The config object is only honored the first time this - is called. Subsequent calls ignore the argument and return the same + Emscripten. (Note the default values for the config object!) The + config object is only honored the first time this is + called. Subsequent calls ignore the argument and return the same (configured) object which gets initialized by the first call. The config object properties include: @@ -133,7 +133,7 @@ */ 'use strict'; self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( - apiConfig = (sqlite3ApiBootstrap.defaultConfig || self.sqlite3ApiConfig) + apiConfig = (self.sqlite3ApiConfig || sqlite3ApiBootstrap.defaultConfig) ){ if(sqlite3ApiBootstrap.sqlite3){ /* already initalized */ console.warn("sqlite3ApiBootstrap() called multiple times.", @@ -567,18 +567,22 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( ) ? !!capi.sqlite3_compileoption_used(optName) : false; }/*compileOptionUsed()*/; - capi.wasm.bindingSignatures = [ - /** - Signatures for the WASM-exported C-side functions. Each entry - is an array with 2+ elements: + /** + Signatures for the WASM-exported C-side functions. Each entry + is an array with 2+ elements: - ["c-side name", - "result type" (capi.wasm.xWrap() syntax), - [arg types in xWrap() syntax] - // ^^^ this needn't strictly be an array: it can be subsequent - // elements instead: [x,y,z] is equivalent to x,y,z - ] - */ + [ "c-side name", + "result type" (capi.wasm.xWrap() syntax), + [arg types in xWrap() syntax] + // ^^^ this needn't strictly be an array: it can be subsequent + // elements instead: [x,y,z] is equivalent to x,y,z + ] + + Note that support for the API-specific data types in the + result/argument type strings gets plugged in at a later phase in + the API initialization process. + */ + capi.wasm.bindingSignatures = [ // Please keep these sorted by function name! ["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*"], ["sqlite3_bind_double","int", "sqlite3_stmt*", "int", "f64"], @@ -604,6 +608,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( "sqlite3*", "string", "int", "int", "*", "*", "*", "*", "*"], ["sqlite3_data_count", "int", "sqlite3_stmt*"], ["sqlite3_db_filename", "string", "sqlite3*", "string"], + ["sqlite3_db_handle", "sqlite3*", "sqlite3_stmt*"], ["sqlite3_db_name", "string", "sqlite3*", "int"], ["sqlite3_errmsg", "string", "sqlite3*"], ["sqlite3_error_offset", "int", "sqlite3*"], @@ -614,6 +619,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( ["sqlite3_expanded_sql", "string", "sqlite3_stmt*"], ["sqlite3_extended_errcode", "int", "sqlite3*"], ["sqlite3_extended_result_codes", "int", "sqlite3*", "int"], + ["sqlite3_file_control", "int", "sqlite3*", "string", "int", "*"], ["sqlite3_finalize", "int", "sqlite3_stmt*"], ["sqlite3_initialize", undefined], ["sqlite3_interrupt", undefined, "sqlite3*" @@ -740,20 +746,132 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( /** Returns true if sqlite3.capi.sqlite3_web_persistent_dir() is a - non-empty string and the given name has that string as its - prefix, else returns false. + non-empty string and the given name starts with (that string + + '/'), else returns false. + + Potential (but arguable) TODO: return true if the name is one of + (":localStorage:", "local", ":sessionStorage:", "session") and + kvvfs is available. */ capi.sqlite3_web_filename_is_persistent = function(name){ const p = capi.sqlite3_web_persistent_dir(); - return (p && name) ? name.startsWith(p) : false; + return (p && name) ? name.startsWith(p+'/') : false; }; - + if(0===capi.wasm.exports.sqlite3_vfs_find(0)){ /* Assume that sqlite3_initialize() has not yet been called. This will be the case in an SQLITE_OS_KV build. */ capi.wasm.exports.sqlite3_initialize(); } + if( self.window===self ){ + /* Features specific to the main window thread... */ + + /** + Internal helper for sqlite3_web_kvvfs_clear() and friends. + Its argument should be one of ('local','session',''). + */ + const __kvvfsInfo = function(which){ + const rc = Object.create(null); + rc.prefix = 'kvvfs-'+which; + rc.stores = []; + if('session'===which || ''===which) rc.stores.push(self.sessionStorage); + if('local'===which || ''===which) rc.stores.push(self.localStorage); + return rc; + }; + + /** + Clears all storage used by the kvvfs DB backend, deleting any + DB(s) stored there. Its argument must be either 'session', + 'local', or ''. In the first two cases, only sessionStorage + resp. localStorage is cleared. If it's an empty string (the + default) then both are cleared. Only storage keys which match + the pattern used by kvvfs are cleared: any other client-side + data are retained. + + This function is only available in the main window thread. + + Returns the number of entries cleared. + */ + capi.sqlite3_web_kvvfs_clear = function(which=''){ + let rc = 0; + const kvinfo = __kvvfsInfo(which); + kvinfo.stores.forEach((s)=>{ + const toRm = [] /* keys to remove */; + let i; + for( i = 0; i < s.length; ++i ){ + const k = s.key(i); + if(k.startsWith(kvinfo.prefix)) toRm.push(k); + } + toRm.forEach((kk)=>s.removeItem(kk)); + rc += toRm.length; + }); + return rc; + }; + + /** + This routine guesses the approximate amount of + window.localStorage and/or window.sessionStorage in use by the + kvvfs database backend. Its argument must be one of + ('session', 'local', ''). In the first two cases, only + sessionStorage resp. localStorage is counted. If it's an empty + string (the default) then both are counted. Only storage keys + which match the pattern used by kvvfs are counted. The returned + value is the "length" value of every matching key and value, + noting that the kvvf uses only ASCII keys and values. + + Note that the returned size is not authoritative from the + perspective of how much data can fit into localStorage and + sessionStorage, as the precise algorithms for determining + those limits are unspecified and may include per-entry + overhead invisible to clients. + */ + capi.sqlite3_web_kvvfs_size = function(which=''){ + let sz = 0; + const kvinfo = __kvvfsInfo(which); + kvinfo.stores.forEach((s)=>{ + let i; + for(i = 0; i < s.length; ++i){ + const k = s.key(i); + if(k.startsWith(kvinfo.prefix)){ + sz += k.length; + sz += s.getItem(k).length; + } + } + }); + return sz; + }; + + /** + Given an `sqlite3*`, returns a truthy value (see below) if that + db handle uses the "kvvfs" VFS, else returns false. If pDb is + NULL then this function returns true if the default VFS is + "kvvfs". Results are undefined if pDb is truthy but refers to + an invalid pointer. + + The truthy value it returns is a pointer to the kvvfs + `sqlite3_vfs` object. + */ + capi.sqlite3_web_db_is_kvvfs = function(pDb){ + const pK = capi.sqlite3_vfs_find("kvvfs"); + if(!pK) return false; + else if(!pDb){ + return capi.sqlite3_vfs_find(0) && pK; + } + const scope = capi.wasm.scopedAllocPush(); + try{ + const ppVfs = capi.wasm.scopedAllocPtr(); + return ( + (0===capi.sqlite3_file_control( + pDb, "main", capi.SQLITE_FCNTL_VFS_POINTER, ppVfs + )) && (capi.wasm.getPtrValue(ppVfs) === pK) + ) ? pK : false; + }finally{ + capi.wasm.scopedAllocPop(scope); + } + }; + }/* main-window-only bits */ + /* The remainder of the API will be set up in later steps. */ const sqlite3 = { WasmAllocError: WasmAllocError, @@ -790,6 +908,11 @@ self.sqlite3ApiBootstrap.initializers = []; global-scope symbol. */ self.sqlite3ApiBootstrap.defaultConfig = Object.create(null); -/** Placeholder: gets installed by the first call to - self.sqlite3ApiBootstrap(). */ +/** + Placeholder: gets installed by the first call to + self.sqlite3ApiBootstrap(). However, it is recommended that the + caller of sqlite3ApiBootstrap() capture its return value and delete + self.sqlite3ApiBootstrap after calling it. It returns the same + value which will be stored here. +*/ self.sqlite3ApiBootstrap.sqlite3 = undefined; diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index 2a505f19ab..c072e8c9d4 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -76,7 +76,7 @@ int sqlite3_wasm_db_error(sqlite3*db, int err_code, const char *zMsg){ */ WASM_KEEP const char * sqlite3_wasm_enum_json(void){ - static char strBuf[1024 * 8] = {0} /* where the JSON goes */; + static char strBuf[1024 * 12] = {0} /* where the JSON goes */; int n = 0, childCount = 0, structCount = 0 /* output counters for figuring out where commas go */; char * pos = &strBuf[1] /* skip first byte for now to help protect @@ -259,7 +259,8 @@ const char * sqlite3_wasm_enum_json(void){ } _DefGroup; DefGroup(openFlags) { - /* Noting that not all of these will have any effect in WASM-space. */ + /* Noting that not all of these will have any effect in + ** WASM-space. */ DefInt(SQLITE_OPEN_READONLY); DefInt(SQLITE_OPEN_READWRITE); DefInt(SQLITE_OPEN_CREATE); @@ -322,6 +323,49 @@ const char * sqlite3_wasm_enum_json(void){ DefInt(SQLITE_IOCAP_BATCH_ATOMIC); } _DefGroup; + DefGroup(fcntl) { + DefInt(SQLITE_FCNTL_LOCKSTATE); + DefInt(SQLITE_FCNTL_GET_LOCKPROXYFILE); + DefInt(SQLITE_FCNTL_SET_LOCKPROXYFILE); + DefInt(SQLITE_FCNTL_LAST_ERRNO); + DefInt(SQLITE_FCNTL_SIZE_HINT); + DefInt(SQLITE_FCNTL_CHUNK_SIZE); + DefInt(SQLITE_FCNTL_FILE_POINTER); + DefInt(SQLITE_FCNTL_SYNC_OMITTED); + DefInt(SQLITE_FCNTL_WIN32_AV_RETRY); + DefInt(SQLITE_FCNTL_PERSIST_WAL); + DefInt(SQLITE_FCNTL_OVERWRITE); + DefInt(SQLITE_FCNTL_VFSNAME); + DefInt(SQLITE_FCNTL_POWERSAFE_OVERWRITE); + DefInt(SQLITE_FCNTL_PRAGMA); + DefInt(SQLITE_FCNTL_BUSYHANDLER); + DefInt(SQLITE_FCNTL_TEMPFILENAME); + DefInt(SQLITE_FCNTL_MMAP_SIZE); + DefInt(SQLITE_FCNTL_TRACE); + DefInt(SQLITE_FCNTL_HAS_MOVED); + DefInt(SQLITE_FCNTL_SYNC); + DefInt(SQLITE_FCNTL_COMMIT_PHASETWO); + DefInt(SQLITE_FCNTL_WIN32_SET_HANDLE); + DefInt(SQLITE_FCNTL_WAL_BLOCK); + DefInt(SQLITE_FCNTL_ZIPVFS); + DefInt(SQLITE_FCNTL_RBU); + DefInt(SQLITE_FCNTL_VFS_POINTER); + DefInt(SQLITE_FCNTL_JOURNAL_POINTER); + DefInt(SQLITE_FCNTL_WIN32_GET_HANDLE); + DefInt(SQLITE_FCNTL_PDB); + DefInt(SQLITE_FCNTL_BEGIN_ATOMIC_WRITE); + DefInt(SQLITE_FCNTL_COMMIT_ATOMIC_WRITE); + DefInt(SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE); + DefInt(SQLITE_FCNTL_LOCK_TIMEOUT); + DefInt(SQLITE_FCNTL_DATA_VERSION); + DefInt(SQLITE_FCNTL_SIZE_LIMIT); + DefInt(SQLITE_FCNTL_CKPT_DONE); + DefInt(SQLITE_FCNTL_RESERVE_BYTES); + DefInt(SQLITE_FCNTL_CKPT_START); + DefInt(SQLITE_FCNTL_EXTERNAL_READER); + DefInt(SQLITE_FCNTL_CKSM_FILE); + } _DefGroup; + DefGroup(access){ DefInt(SQLITE_ACCESS_EXISTS); DefInt(SQLITE_ACCESS_READWRITE); diff --git a/ext/wasm/batch-runner.html b/ext/wasm/batch-runner.html index 38f38070c0..2a6c1405cf 100644 --- a/ext/wasm/batch-runner.html +++ b/ext/wasm/batch-runner.html @@ -55,7 +55,9 @@
- + + diff --git a/ext/wasm/batch-runner.js b/ext/wasm/batch-runner.js index 437424b48f..9964d747c7 100644 --- a/ext/wasm/batch-runner.js +++ b/ext/wasm/batch-runner.js @@ -17,6 +17,7 @@ (function(){ const toss = function(...args){throw new Error(args.join(' '))}; const warn = console.warn.bind(console); + let sqlite3; const App = { e: { @@ -30,6 +31,7 @@ btnReset: document.querySelector('#db-reset'), cbReverseLog: document.querySelector('#cb-reverse-log-order') }, + db: Object.create(null), cache:{}, metrics:{ /** @@ -61,15 +63,16 @@ }, openDb: function(fn, unlinkFirst=true){ - if(this.db && this.db.ptr){ + if(this.db.ptr){ toss("Already have an opened db."); } const capi = this.sqlite3.capi, wasm = capi.wasm; const stack = wasm.scopedAllocPush(); let pDb = 0; try{ - if(unlinkFirst && fn && ':memory:'!==fn){ - capi.wasm.sqlite3_wasm_vfs_unlink(fn); + if(unlinkFirst && fn){ + if(':'!==fn[0]) capi.wasm.sqlite3_wasm_vfs_unlink(fn); + this.clearStorage(); } const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; const ppDb = wasm.scopedAllocPtr(); @@ -82,7 +85,6 @@ }finally{ wasm.scopedAllocPop(stack); } - this.db = Object.create(null); this.db.filename = fn; this.db.ptr = pDb; this.logHtml("Opened db:",fn); @@ -90,10 +92,13 @@ }, closeDb: function(unlink=false){ - if(this.db && this.db.ptr){ + if(this.db.ptr){ this.sqlite3.capi.sqlite3_close_v2(this.db.ptr); this.logHtml("Closed db",this.db.filename); - if(unlink) capi.wasm.sqlite3_wasm_vfs_unlink(this.db.filename); + if(unlink){ + capi.wasm.sqlite3_wasm_vfs_unlink(this.db.filename); + this.clearStorage(); + } this.db.ptr = this.db.filename = undefined; } }, @@ -329,6 +334,21 @@ return p.catch((e)=>this.logErr("Error via evalFile("+fn+"):",e.message)); }/*evalFile()*/, + clearStorage: function(){ + const sz = sqlite3.capi.sqlite3_web_kvvfs_size(); + const n = sqlite3.capi.sqlite3_web_kvvfs_clear(this.db.filename || ''); + this.logHtml("Cleared kvvfs local/sessionStorage:", + n,"entries totaling approximately",sz,"bytes."); + }, + + resetDb: function(){ + if(this.db.ptr){ + const fn = this.db.filename; + this.closeDb(true); + this.openDb(fn,false); + } + }, + run: function(sqlite3){ delete this.run; this.sqlite3 = sqlite3; @@ -336,9 +356,14 @@ this.logHtml("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); this.logHtml("WASM heap size =",wasm.heap8().length); this.loadSqlList(); - const pDir = capi.sqlite3_web_persistent_dir(); - const dbFile = pDir ? pDir+"/speedtest.db" : ":memory:"; - if(!pDir){ + const pDir = 1 ? '' : capi.sqlite3_web_persistent_dir(); + const dbFile = pDir ? pDir+"/speedtest.db" : ( + sqlite3.capi.sqlite3_vfs_find('kvvfs') ? 'local' : ':memory:' + ); + this.clearStorage(); + if(pDir){ + logHtml("Using persistent storage:",dbFile); + }else{ document.querySelector('#warn-opfs').remove(); } this.openDb(dbFile, !!pDir); @@ -368,11 +393,7 @@ who.evalFile(who.e.selSql.value); }, false); this.e.btnReset.addEventListener('click', function(){ - const fn = who.db.filename; - if(fn){ - who.closeDb(true); - who.openDb(fn,true); - } + who.resetDb(); }, false); this.e.btnExportMetrics.addEventListener('click', function(){ who.logHtml2('warning',"Triggering download of metrics CSV. Check your downloads folder."); @@ -394,12 +415,14 @@ } const timeTotal = performance.now() - timeStart; who.logHtml("Run-remaining time:",timeTotal,"ms ("+(timeTotal/1000/60)+" minute(s))"); + who.clearStorage(); }, false); }/*run()*/ }/*App*/; self.sqlite3TestModule.initSqlite3().then(function(theEmccModule){ self._MODULE = theEmccModule /* this is only to facilitate testing from the console */; + sqlite3 = theEmccModule.sqlite3; App.run(theEmccModule.sqlite3); }); })(); diff --git a/ext/wasm/kvvfs1.html b/ext/wasm/kvvfs1.html index 1a5eb62cf1..5d9f077958 100644 --- a/ext/wasm/kvvfs1.html +++ b/ext/wasm/kvvfs1.html @@ -31,6 +31,7 @@ +
diff --git a/ext/wasm/kvvfs1.js b/ext/wasm/kvvfs1.js index c29426fc63..fd57051a26 100644 --- a/ext/wasm/kvvfs1.js +++ b/ext/wasm/kvvfs1.js @@ -48,35 +48,35 @@ wasm = capi.wasm; log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); T.assert( 0 !== capi.sqlite3_vfs_find(null) ); - if(!oo.DB.clearKvvfsStorage){ + if(!capi.sqlite3_vfs_find('kvvfs')){ warn("This build is not kvvfs-capable."); return; } - const dbStorage = 1 ? ':sessionStorage:' : ':localStorage:'; - const theStore = 's'===dbStorage[1] ? sessionStorage : localStorage; + const dbStorage = 1 ? 'session' : 'local'; + const theStore = 's'===dbStorage[0] ? sessionStorage : localStorage; /** The names ':sessionStorage:' and ':localStorage:' are handled via the DB class constructor, not the C level. In the C API, the names "local" and "session" are the current (2022-09-12) names for those keys, but that is subject to change. */ - const db = new oo.DB( dbStorage ); + const db = new oo.DB( dbStorage, 'c', 'kvvfs' ); document.querySelector('#btn-clear-storage').addEventListener('click',function(){ - oo.DB.clearKvvfsStorage(); - log("kvvfs localStorage and sessionStorage cleared."); + const sz = capi.sqlite3_web_kvvfs_clear(); + log("kvvfs localStorage and sessionStorage cleared:",sz,"entries."); }); document.querySelector('#btn-clear-log').addEventListener('click',function(){ eOutput.innerText = ''; }); document.querySelector('#btn-init-db').addEventListener('click',function(){ - const saveSql = []; try{ + const saveSql = []; db.exec({ - sql:["drop table if exists t;", - "create table if not exists t(a);", - "insert into t(a) values(?),(?),(?)"], + sql: ["drop table if exists t;", + "create table if not exists t(a);", + "insert into t(a) values(?),(?),(?)"], bind: [performance.now() >> 0, (performance.now() * 2) >> 0, (performance.now() / 2) >> 0], @@ -101,6 +101,10 @@ error(e.message); } }); + document.querySelector('#btn-storage-size').addEventListener('click',function(){ + log("sqlite3_web_kvvfs_size(",dbStorage,") says", capi.sqlite3_web_kvvfs_size(dbStorage), + "bytes"); + }); log("Storage backend:",db.filename /* note that the name was internally translated */); if(0===db.selectValue('select count(*) from sqlite_master')){ log("DB is empty. Use the init button to populate it."); diff --git a/ext/wasm/testing1.js b/ext/wasm/testing1.js index 2aadc30c3a..e45b52bfa0 100644 --- a/ext/wasm/testing1.js +++ b/ext/wasm/testing1.js @@ -33,6 +33,8 @@ return v1>=(v2-factor) && v1<=(v2+factor); }; + let sqlite3; + const testBasicSanity = function(db,sqlite3){ const capi = sqlite3.capi; log("Basic sanity tests..."); @@ -1005,10 +1007,17 @@ } }/*testWasmUtil()*/; + const clearKvvfs = function(){ + const sz = sqlite3.capi.sqlite3_web_kvvfs_size(); + const n = sqlite3.capi.sqlite3_web_kvvfs_clear(''); + log("Cleared kvvfs local/sessionStorage:", + n,"entries totaling approximately",sz,"bytes."); + }; + const runTests = function(Module){ //log("Module",Module); - const sqlite3 = Module.sqlite3, - capi = sqlite3.capi, + sqlite3 = Module.sqlite3; + const capi = sqlite3.capi, oo = sqlite3.oo1, wasm = capi.wasm; log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); @@ -1048,14 +1057,15 @@ let dbName = "/testing1.sqlite3"; let vfsName = undefined; - if(oo.DB.clearKvvfsStorage){ + if(capi.sqlite3_web_db_is_kvvfs()){ dbName = "local"; vfsName = 'kvvfs'; logHtml("Found kvvfs. Clearing db(s) from sessionStorage and localStorage", "and selecting kvvfs-friendly db name:",dbName); - oo.DB.clearKvvfsStorage(); + clearKvvfs(); } const db = new oo.DB(dbName,'c',vfsName), startTime = performance.now(); + log("capi.sqlite3_web_db_is_kvvfs() ==",capi.sqlite3_web_db_is_kvvfs(db.pointer)); try { log("db.filename =",db.filename,"db.fileName() =",db.fileName()); const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', @@ -1072,6 +1082,7 @@ }); }finally{ db.close(); + if('kvvfs'===vfsName) clearKvvfs(); } logHtml("Total Test count:",T.counter,"in",(performance.now() - startTime),"ms"); log('capi.wasm.exports',capi.wasm.exports); diff --git a/manifest b/manifest index b104be806b..6fa5a9fc8a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Get\stesting1.js\sworking\swith\sa\skvvfs\sbuild. -D 2022-09-12T22:27:00.037 +C Add/apply\svarious\skvvfs-specific\sutility\sAPIs\sto\sthe\sJS\slayer\sto\sassist\sin\stesting\sand\sanalysis.\sCorrect\sa\sbackwards\sdefault\sarg\scheck\sfor\ssqlite3ApiBootstrap().\sAdd\sexports\sfor\ssqlite3_db_handle(),\ssqlite3_file_control(),\sand\sthe\sSQLITE_FCNTL_xxx\senum. +D 2022-09-13T19:27:03.599 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -476,21 +476,21 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f057 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 F ext/wasm/GNUmakefile b175a00599c976fe9e7bc02bbc1337b5e3b81d042320c3a0be1621d2c7a21b21 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 -F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 1dfd067b3cbd9a49cb204097367cf2f8fe71b5a3b245d9d82a24779fd4ac2394 +F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 150a793a47205b8009ac934f3b6d6ebf67b965c072339aaa25ce808a19e116cc F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 F ext/wasm/api/README.md d876597edd2b9542b6ea031adaaff1c042076fde7b670b1dc6d8a87b28a6631b F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b -F ext/wasm/api/sqlite3-api-cleanup.js 101919ec261644e2f6f0a59952fd9612127b69ea99b493277b2789ea478f9b6b -F ext/wasm/api/sqlite3-api-glue.js 2bf536a38cde324cf352bc2c575f8e22c6d204d667c0eda5a254ba45318914bc -F ext/wasm/api/sqlite3-api-oo1.js b498662748918c132aa59433ea2bd2ebb7e026549fd68506a1ae1ea94736f4f6 +F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77 +F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 +F ext/wasm/api/sqlite3-api-oo1.js 4925f4736eb28fd3a6d4dbbd2c078c38be651aa5996041e7f0b81be36506608e F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 -F ext/wasm/api/sqlite3-api-prologue.js 9e37ce4dfd74926d0df80dd7e72e33085db4bcee48e2c21236039be416a7dff2 +F ext/wasm/api/sqlite3-api-prologue.js c496adc0cf2ae427ee900aad01c09db97850bd8b6737f2849cab207c8415b839 F ext/wasm/api/sqlite3-api-worker1.js d33062afa045fd4be01ba4abc266801807472558b862b30056211b00c9c347b4 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 -F ext/wasm/api/sqlite3-wasm.c bf4637cf28463cada4b25f09651943c7ece004b253ef39b7ab68eaa60662aa09 -F ext/wasm/batch-runner.html 23209ade7981acce7ecd79d6eff9f4c5a4e8b14ae867ac27cd89b230be640fa6 -F ext/wasm/batch-runner.js 2abd146d3e3a66128ac0a2cc39bfd01e9811c9511fa10ec927d6649795f1ee50 +F ext/wasm/api/sqlite3-wasm.c 7d1760f3a864a9ae16cfb71543829d946a54384bd07af99a3adf9ef32913705f +F ext/wasm/batch-runner.html 2857a6db7292ac83d1581af865d643fd34235db2df830d10b43b01388c599e04 +F ext/wasm/batch-runner.js fb6a338aeef509f181f499700a8595bc578bbc54ef832e0cda7e7e7c10b90a18 F ext/wasm/common/SqliteTestUtil.js 529161a624265ba84271a52db58da022649832fa1c71309fb1e02cc037327a2b F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 3a5143699c2b73a85b962271e1a9b3241b30d90e30d895e4f55665e648572962 @@ -507,8 +507,8 @@ F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356a F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19 F ext/wasm/kvvfs.make bfa0aaac384d9f200d2c8e31efb3536b40d139667b88e6eba9c0a71e23da6a5c -F ext/wasm/kvvfs1.html 83bac238d1e93ed102a461672fc58fe74e611e426708e9a5c66002c01eadb942 -F ext/wasm/kvvfs1.js 53721a42e0ec45f6978cc723e5de47f882517867d0fcff4c6ff05f4c422e27c4 +F ext/wasm/kvvfs1.html 13bb24190bfb276a57b228499519badcc1bf39ed07e4b37bc2a425ce6418fed1 +F ext/wasm/kvvfs1.js a23ee98a78b9da379eab7a20d7ddf5283a684dd7a91685a84686c05216f96689 F ext/wasm/scratchpad-opfs-main.html 4565cf194e66188190d35f70e82553e2e2d72b9809b73c94ab67b8cfd14d2e0c F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 @@ -526,7 +526,7 @@ F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416 F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 F ext/wasm/testing-worker1-promiser.js c62b5879339eef0b21aebd9d75bc125c86530edc17470afff18077f931cb704a F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae -F ext/wasm/testing1.js 735120231d6e55b92964924fcb54a66484d8b2e30fe5a18c71081b3590344a5f +F ext/wasm/testing1.js 7cd8ab255c238b030d928755ae8e91e7d90a12f2ae601b1b8f7827aaa4fb258e F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x @@ -2024,8 +2024,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 8b1608a9a37131121cb3a0cb1e8dbad5e39dc1fc2a341d056f09537f179b2e7a -R 77095a41dfb9b784dcab0078822e6eff +P 333a45725d1708e0fefa559c33ce1c7eeb425cdb04f594b1f2b48166c1478c79 +R fff963b52d5c3a76677d96f35303ab67 U stephan -Z c624cb9efadffe8972e3df15b8dc9939 +Z e8c548c1774e1a39daa4183bad155966 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 69b3e88fd2..e142fcae1f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -333a45725d1708e0fefa559c33ce1c7eeb425cdb04f594b1f2b48166c1478c79 \ No newline at end of file +0d78961870ee9f22f1ba16d423377d28dcc36e04b1e31ffd57f3e2fd51f8f0f2 \ No newline at end of file From a3a6d63b4087895764ce15a8cf2e6afde76b6f9a Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 13 Sep 2022 19:47:01 +0000 Subject: [PATCH 101/428] Fix a broken error handling case in the recovery extension. FossilOrigin-Name: 60089547e1fc77ecc02f207ebf75ee3160e5ff25f41d12e02e170fd7fde66602 --- ext/recover/recoverfault.test | 1 + ext/recover/sqlite3recover.c | 11 ++++++++--- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/ext/recover/recoverfault.test b/ext/recover/recoverfault.test index 0241034100..e6d8392a44 100644 --- a/ext/recover/recoverfault.test +++ b/ext/recover/recoverfault.test @@ -49,6 +49,7 @@ do_execsql_test 1.0 { faultsim_save_and_close do_faultsim_test 1 -faults oom* -prep { + catch { db2 close } faultsim_restore_and_reopen } -body { set R [sqlite3_recover_init db main test.db2] diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index 206a6fad5b..c67d9b3d49 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -495,9 +495,9 @@ static void recoverReadI32( assert( argc==2 ); nBlob = sqlite3_value_bytes(argv[0]); pBlob = (const unsigned char*)sqlite3_value_blob(argv[0]); - iInt = sqlite3_value_int(argv[1]); + iInt = sqlite3_value_int(argv[1]) & 0xFFFF; - if( iInt>=0 && (iInt+1)*4<=nBlob ){ + if( (iInt+1)*4<=nBlob ){ const unsigned char *a = &pBlob[iInt*4]; i64 iVal = ((i64)a[0]<<24) + ((i64)a[1]<<16) @@ -770,7 +770,11 @@ static int recoverOpenOutput(sqlite3_recover *p){ if( pBackup ){ while( sqlite3_backup_step(pBackup, 1000)==SQLITE_OK ); p->errCode = sqlite3_backup_finish(pBackup); + }else{ + recoverDbError(p, db); } + }else{ + recoverDbError(p, db2); } sqlite3_close(db2); } @@ -1791,7 +1795,7 @@ int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){ p->zStateDb = recoverMPrintf(p, "%s", (char*)pArg); break; - case SQLITE_RECOVER_LOST_AND_FOUND: + case SQLITE_RECOVER_LOST_AND_FOUND: { const char *zArg = (const char*)pArg; sqlite3_free(p->zLostAndFound); if( zArg ){ @@ -1800,6 +1804,7 @@ int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){ p->zLostAndFound = 0; } break; + } case SQLITE_RECOVER_FREELIST_CORRUPT: p->bFreelistCorrupt = *(int*)pArg; diff --git a/manifest b/manifest index 3f6d9ab674..954e80712a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\stests.\sDeal\sissues\ssurrounding\saborting\srecovery\sfrom\swithin\sthe\sSQL\scallback,\sand\savoiding\sthe\spending-byte\spage. -D 2022-09-13T18:08:24.770 +C Fix\sa\sbroken\serror\shandling\scase\sin\sthe\srecovery\sextension. +D 2022-09-13T19:47:01.338 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -391,11 +391,11 @@ F ext/recover/recover1.test d0fa2f945aac4754e0abb802941b9b80efd4828a775a090f9d22 F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c F ext/recover/recoverclobber.test 294dcc894124ab4ca3a7b35766630742a3d25810fceac22220beb64f70a33a60 F ext/recover/recovercorrupt.test e3f3cbe0162ba681518aac9ea0ae8119f32ac93fb0900b5f09b6318966108e54 -F ext/recover/recoverfault.test 862ab02bd503922281a5ee9f81a3a92312561bb7a39e2fdb06e6afd60c3a167e +F ext/recover/recoverfault.test f3587c218c448545a082b99d59294dff5ec0b7daa15b0556cf926f6c350f221e F ext/recover/recoverold.test 46e9d99b595fac583d4c67f74d7d89c20a435c752ef6eeb3e918b599940c88e0 F ext/recover/recoverrowid.test 1694a1a5526d825f71279f3d02ab02a1ee4c5265de18858bf54cb8ec54487ac8 F ext/recover/recoversql.test f9872ff2114e13ffd8ee31e1de06919f62b9b48bc080191b5bd076d10becb60f -F ext/recover/sqlite3recover.c ed5ab827433823ebf3ad7ada192ecefdc8ef9327af68fd4db9ae463a11bbc476 +F ext/recover/sqlite3recover.c 1afcac2bbfcf5ef67a3391f59678dc866348ac88745067e683052ede02c425fb F ext/recover/sqlite3recover.h 81108efb8c4618d3d9c6da4df785212b0e4501aa0d25edfc463405fe839a6640 F ext/recover/test_recover.c 5941ecf484b6158be26e34c6f7b6c7f03967c72a63db0c08e7fd0fa43023eafa F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 @@ -2010,8 +2010,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P ed318be8241981ef96334ba13d3201a717cc812a59aed64e3dc67f7e7e71854b -R 11a8fecaa81e021bbb20605615a33134 +P 4e97dd31f5240d9231167ae172a5116426c42177a1ed3c5422b9d51b762d5a87 +R bab9a5e0534df0c970f1689fb0655c4b U dan -Z 28892e8b77cb290f00759179724671d9 +Z ca04715ddf53f08e8c8270238268a615 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9694fdd0c2..d9dc13270c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4e97dd31f5240d9231167ae172a5116426c42177a1ed3c5422b9d51b762d5a87 \ No newline at end of file +60089547e1fc77ecc02f207ebf75ee3160e5ff25f41d12e02e170fd7fde66602 \ No newline at end of file From 322967df59cf549e6836f45cf38bc2dcb81f35b1 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 13 Sep 2022 20:40:57 +0000 Subject: [PATCH 102/428] Add OOM tests for the recovery extension. FossilOrigin-Name: 9b6b4c7162439a889144edb561356afc66436db921a867c20871f0c556716502 --- ext/recover/recover1.test | 26 ++++++++ ext/recover/recoverfault2.test | 108 +++++++++++++++++++++++++++++++++ manifest | 13 ++-- manifest.uuid | 2 +- 4 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 ext/recover/recoverfault2.test diff --git a/ext/recover/recover1.test b/ext/recover/recover1.test index 3ee4c18ad6..e7a45e8e90 100644 --- a/ext/recover/recover1.test +++ b/ext/recover/recover1.test @@ -167,6 +167,32 @@ ifcapable fts5 { do_recover_test 9 } +#------------------------------------------------------------------------- +reset_db +do_execsql_test 10.1 { + CREATE TABLE x1(a PRIMARY KEY, str TEXT) WITHOUT ROWID; + INSERT INTO x1 VALUES(1, ' + \nhello\012world(\n0)(\n1) + '); + INSERT INTO x1 VALUES(2, ' + \nhello + '); +} +do_execsql_test 10.2 " + INSERT INTO x1 VALUES(3, '\012hello there\015world'); + INSERT INTO x1 VALUES(4, '\015hello there\015world'); +" +do_recover_test 10 + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 11.1 { + PRAGMA encoding='utf16'; + CREATE TABLE u1(u, v); + INSERT INTO u1 VALUES('edvin marton', 'bond'); + INSERT INTO u1 VALUES(1, 4.0); +} +do_recover_test 11 diff --git a/ext/recover/recoverfault2.test b/ext/recover/recoverfault2.test new file mode 100644 index 0000000000..7489206050 --- /dev/null +++ b/ext/recover/recoverfault2.test @@ -0,0 +1,108 @@ +# 2022 August 28 +# +# 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. +# +#*********************************************************************** +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] recover_common.tcl] +source $testdir/tester.tcl + +set testprefix recoverfault2 + + +#-------------------------------------------------------------------------- +proc compare_result {db1 db2 sql} { + set r1 [$db1 eval $sql] + set r2 [$db2 eval $sql] + if {$r1 != $r2} { + puts "r1: $r1" + puts "r2: $r2" + error "mismatch for $sql" + } + return "" +} + +proc compare_dbs {db1 db2} { + compare_result $db1 $db2 "SELECT sql FROM sqlite_master ORDER BY 1" + foreach tbl [$db1 eval {SELECT name FROM sqlite_master WHERE type='table'}] { + compare_result $db1 $db2 "SELECT * FROM $tbl" + } +} +#-------------------------------------------------------------------------- + +do_execsql_test 1.0 " + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(2, '\012hello\015world\012today\n'); +" +faultsim_save_and_close + +proc my_sql_hook {sql} { + lappend ::lSql $sql + return 0 +} + +do_faultsim_test 1 -faults oom* -prep { + catch { db2 close } + faultsim_restore_and_reopen + set ::lSql [list] +} -body { + set R [sqlite3_recover_init_sql db main my_sql_hook] + $R run + $R finish +} -test { + faultsim_test_result {0 {}} {1 {}} + if {$testrc==0} { + sqlite3 db2 "" + db2 eval [join $::lSql ";"] + compare_dbs db db2 + db2 close + } +} + +ifcapable utf16 { + reset_db + do_execsql_test 2.0 " + PRAGMA encoding='utf-16'; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(2, '\012hello\015world\012today\n'); + " + faultsim_save_and_close + + proc my_sql_hook {sql} { + puts "HOOK $sql" + lappend ::lSql $sql + return 0 + } + + do_faultsim_test 2 -faults oom-t* -prep { + catch { db2 close } + faultsim_restore_and_reopen + set ::lSql [list] + } -body { + set R [sqlite3_recover_init_sql db main my_sql_hook] + $R run + $R finish + } -test { + faultsim_test_result {0 {}} {1 {}} + if {$testrc==0} { + sqlite3 db2 "" + db2 eval [join $::lSql ";"] + compare_dbs db db2 + db2 close + } + } +} + + + +finish_test + diff --git a/manifest b/manifest index 954e80712a..18c614dd00 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sbroken\serror\shandling\scase\sin\sthe\srecovery\sextension. -D 2022-09-13T19:47:01.338 +C Add\sOOM\stests\sfor\sthe\srecovery\sextension. +D 2022-09-13T20:40:57.096 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -387,11 +387,12 @@ F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2 F ext/rbu/sqlite3rbu.c 8737cabdfbee84bb25a7851ecef8b1312be332761238da9be6ddb10c62ad4291 F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812 F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a -F ext/recover/recover1.test d0fa2f945aac4754e0abb802941b9b80efd4828a775a090f9d2253f5a0ee1d0e +F ext/recover/recover1.test 623afa77b91996bb1319b069ced5245c243caa995f66793d6879fbc2c190f0be F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c F ext/recover/recoverclobber.test 294dcc894124ab4ca3a7b35766630742a3d25810fceac22220beb64f70a33a60 F ext/recover/recovercorrupt.test e3f3cbe0162ba681518aac9ea0ae8119f32ac93fb0900b5f09b6318966108e54 F ext/recover/recoverfault.test f3587c218c448545a082b99d59294dff5ec0b7daa15b0556cf926f6c350f221e +F ext/recover/recoverfault2.test 699b3ec07ba6982291e65e1811807f18d7f115234b407c819eaf3529878867f5 F ext/recover/recoverold.test 46e9d99b595fac583d4c67f74d7d89c20a435c752ef6eeb3e918b599940c88e0 F ext/recover/recoverrowid.test 1694a1a5526d825f71279f3d02ab02a1ee4c5265de18858bf54cb8ec54487ac8 F ext/recover/recoversql.test f9872ff2114e13ffd8ee31e1de06919f62b9b48bc080191b5bd076d10becb60f @@ -2010,8 +2011,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 4e97dd31f5240d9231167ae172a5116426c42177a1ed3c5422b9d51b762d5a87 -R bab9a5e0534df0c970f1689fb0655c4b +P 60089547e1fc77ecc02f207ebf75ee3160e5ff25f41d12e02e170fd7fde66602 +R 4e62121f5a47cd1728b6e036c3f3c8c0 U dan -Z ca04715ddf53f08e8c8270238268a615 +Z 5fc633ab904798afb52a452a7e949f53 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d9dc13270c..2c3276310d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -60089547e1fc77ecc02f207ebf75ee3160e5ff25f41d12e02e170fd7fde66602 \ No newline at end of file +9b6b4c7162439a889144edb561356afc66436db921a867c20871f0c556716502 \ No newline at end of file From b8eaf9a10dbbcb217ffa5c85f2f2c99f9c584dc6 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 14 Sep 2022 16:37:59 +0000 Subject: [PATCH 103/428] Fix recovery of utf-16 databases. FossilOrigin-Name: 5b05be0861f35804270fbd184ad4b89c23e98cc2fbd56b9e4fe6197daef5fe49 --- ext/misc/dbdata.c | 61 ++++++++++++++++++++++++++++------ ext/recover/recover1.test | 1 + ext/recover/recoverfault2.test | 1 - ext/recover/test_recover.c | 35 +++++++++++++++++++ manifest | 20 +++++------ manifest.uuid | 2 +- test/tester.tcl | 41 +++++++++++++++++++++++ 7 files changed, 138 insertions(+), 23 deletions(-) diff --git a/ext/misc/dbdata.c b/ext/misc/dbdata.c index a18304b96b..29a6497bef 100644 --- a/ext/misc/dbdata.c +++ b/ext/misc/dbdata.c @@ -75,6 +75,7 @@ #include "sqlite3ext.h" typedef unsigned char u8; +typedef unsigned int u32; #endif SQLITE_EXTENSION_INIT1 @@ -107,6 +108,7 @@ struct DbdataCursor { int iField; /* Current field number */ u8 *pHdrPtr; u8 *pPtr; + u32 enc; /* Text encoding */ sqlite3_int64 iIntkey; /* Integer key value */ }; @@ -299,14 +301,14 @@ static int dbdataClose(sqlite3_vtab_cursor *pCursor){ /* ** Utility methods to decode 16 and 32-bit big-endian unsigned integers. */ -static unsigned int get_uint16(unsigned char *a){ +static u32 get_uint16(unsigned char *a){ return (a[0]<<8)|a[1]; } -static unsigned int get_uint32(unsigned char *a){ - return ((unsigned int)a[0]<<24) - | ((unsigned int)a[1]<<16) - | ((unsigned int)a[2]<<8) - | ((unsigned int)a[3]); +static u32 get_uint32(unsigned char *a){ + return ((u32)a[0]<<24) + | ((u32)a[1]<<16) + | ((u32)a[2]<<8) + | ((u32)a[3]); } /* @@ -321,7 +323,7 @@ static unsigned int get_uint32(unsigned char *a){ */ static int dbdataLoadPage( DbdataCursor *pCsr, /* Cursor object */ - unsigned int pgno, /* Page number of page to load */ + u32 pgno, /* Page number of page to load */ u8 **ppPage, /* OUT: pointer to page buffer */ int *pnPage /* OUT: Size of (*ppPage) in bytes */ ){ @@ -405,6 +407,7 @@ static int dbdataValueBytes(int eType){ */ static void dbdataValue( sqlite3_context *pCtx, + u32 enc, int eType, u8 *pData, int nData @@ -449,7 +452,17 @@ static void dbdataValue( default: { int n = ((eType-12) / 2); if( eType % 2 ){ - sqlite3_result_text(pCtx, (const char*)pData, n, SQLITE_TRANSIENT); + switch( enc ){ + case SQLITE_UTF16BE: + sqlite3_result_text16be(pCtx, (void*)pData, n, SQLITE_TRANSIENT); + break; + case SQLITE_UTF16LE: + sqlite3_result_text16le(pCtx, (void*)pData, n, SQLITE_TRANSIENT); + break; + default: + sqlite3_result_text(pCtx, (char*)pData, n, SQLITE_TRANSIENT); + break; + } }else{ sqlite3_result_blob(pCtx, pData, n, SQLITE_TRANSIENT); } @@ -588,7 +601,7 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){ /* Load content from overflow pages */ if( nPayload>nLocal ){ sqlite3_int64 nRem = nPayload - nLocal; - unsigned int pgnoOvfl = get_uint32(&pCsr->aPage[iOff]); + u32 pgnoOvfl = get_uint32(&pCsr->aPage[iOff]); while( nRem>0 ){ u8 *aOvfl = 0; int nOvfl = 0; @@ -703,6 +716,25 @@ static int dbdataDbsize(DbdataCursor *pCsr, const char *zSchema){ return rc; } +/* +** Attempt to figure out the encoding of the database by retrieving page 1 +** and inspecting the header field. If successful, set the pCsr->enc variable +** and return SQLITE_OK. Otherwise, return an SQLite error code. +*/ +static int dbdataGetEncoding(DbdataCursor *pCsr){ + int rc = SQLITE_OK; + int nPg1 = 0; + u8 *aPg1 = 0; + rc = dbdataLoadPage(pCsr, 1, &aPg1, &nPg1); + assert( rc!=SQLITE_OK || nPg1==0 || nPg1>=512 ); + if( rc==SQLITE_OK && nPg1>0 ){ + pCsr->enc = get_uint32(&aPg1[56]); + } + sqlite3_free(aPg1); + return rc; +} + + /* ** xFilter method for sqlite_dbdata and sqlite_dbptr. */ @@ -725,7 +757,6 @@ static int dbdataFilter( pCsr->iPgno = sqlite3_value_int(argv[(idxNum & 0x01)]); pCsr->bOnePage = 1; }else{ - pCsr->nPage = dbdataDbsize(pCsr, zSchema); rc = dbdataDbsize(pCsr, zSchema); } @@ -754,6 +785,13 @@ static int dbdataFilter( }else{ pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db)); } + + /* Try to determine the encoding of the db by inspecting the header + ** field on page 1. */ + if( rc==SQLITE_OK ){ + rc = dbdataGetEncoding(pCsr); + } + if( rc==SQLITE_OK ){ rc = dbdataNext(pCursor); } @@ -808,7 +846,8 @@ static int dbdataColumn( sqlite3_int64 iType; dbdataGetVarint(pCsr->pHdrPtr, &iType); dbdataValue( - ctx, iType, pCsr->pPtr, &pCsr->pRec[pCsr->nRec] - pCsr->pPtr + ctx, pCsr->enc, iType, pCsr->pPtr, + &pCsr->pRec[pCsr->nRec] - pCsr->pPtr ); } break; diff --git a/ext/recover/recover1.test b/ext/recover/recover1.test index e7a45e8e90..358015a591 100644 --- a/ext/recover/recover1.test +++ b/ext/recover/recover1.test @@ -192,6 +192,7 @@ do_execsql_test 11.1 { INSERT INTO u1 VALUES('edvin marton', 'bond'); INSERT INTO u1 VALUES(1, 4.0); } + do_recover_test 11 diff --git a/ext/recover/recoverfault2.test b/ext/recover/recoverfault2.test index 7489206050..4f7131ecfb 100644 --- a/ext/recover/recoverfault2.test +++ b/ext/recover/recoverfault2.test @@ -78,7 +78,6 @@ ifcapable utf16 { faultsim_save_and_close proc my_sql_hook {sql} { - puts "HOOK $sql" lappend ::lSql $sql return 0 } diff --git a/ext/recover/test_recover.c b/ext/recover/test_recover.c index b273fbcd1d..7ab440fa37 100644 --- a/ext/recover/test_recover.c +++ b/ext/recover/test_recover.c @@ -229,6 +229,40 @@ static int test_sqlite3_recover_init( return TCL_OK; } +/* +** Declaration for public API function in file dbdata.c. This may be called +** with NULL as the final two arguments to register the sqlite_dbptr and +** sqlite_dbdata virtual tables with a database handle. +*/ +#ifdef _WIN32 +__declspec(dllexport) +#endif +int sqlite3_dbdata_init(sqlite3*, char**, const sqlite3_api_routines*); + +/* +** sqlite3_recover_init DB DBNAME URI +*/ +static int test_sqlite3_dbdata_init( + void *clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3 *db = 0; + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB"); + return TCL_ERROR; + } + if( getDbPointer(interp, objv[1], &db) ) return TCL_ERROR; + sqlite3_dbdata_init(db, 0, 0); + + Tcl_ResetResult(interp); + return TCL_OK; +} + + + int TestRecover_Init(Tcl_Interp *interp){ struct Cmd { const char *zCmd; @@ -237,6 +271,7 @@ int TestRecover_Init(Tcl_Interp *interp){ } aCmd[] = { { "sqlite3_recover_init", test_sqlite3_recover_init, 0 }, { "sqlite3_recover_init_sql", test_sqlite3_recover_init, (void*)1 }, + { "sqlite3_dbdata_init", test_sqlite3_dbdata_init, (void*)1 }, }; int i; diff --git a/manifest b/manifest index 18c614dd00..b17390fa0e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sOOM\stests\sfor\sthe\srecovery\sextension. -D 2022-09-13T20:40:57.096 +C Fix\srecovery\sof\sutf-16\sdatabases. +D 2022-09-14T16:37:59.285 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -299,7 +299,7 @@ F ext/misc/closure.c dbfd8543b2a017ae6b1a5843986b22ddf99ff126ec9634a2f4047cd14c8 F ext/misc/completion.c 6dafd7f4348eecc7be9e920d4b419d1fb2af75d938cd9c59a20cfe8beb2f22b9 F ext/misc/compress.c 3354c77a7c8e86e07d849916000cdac451ed96500bfb5bd83b20eb61eee012c9 F ext/misc/csv.c ca8d6dafc5469639de81937cb66ae2e6b358542aba94c4f791910d355a8e7f73 -F ext/misc/dbdata.c 9bb3666519bd8a54cce4934076a557fe6441c5bafce7e9c24d8b5ced148e8154 +F ext/misc/dbdata.c ca7b235fa2396e8fc2e950826872f820f31268ac2cb51368b0d655bb71568f07 F ext/misc/dbdump.c b8592f6f2da292c62991a13864a60d6c573c47a9cc58362131b9e6a64f823e01 F ext/misc/decimal.c 09f967dcf4a1ee35a76309829308ec278d3648168733f4a1147820e11ebefd12 F ext/misc/eval.c 04bc9aada78c888394204b4ed996ab834b99726fb59603b0ee3ed6e049755dc1 @@ -387,18 +387,18 @@ F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2 F ext/rbu/sqlite3rbu.c 8737cabdfbee84bb25a7851ecef8b1312be332761238da9be6ddb10c62ad4291 F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812 F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a -F ext/recover/recover1.test 623afa77b91996bb1319b069ced5245c243caa995f66793d6879fbc2c190f0be +F ext/recover/recover1.test 167ad4b267f6db5c06963a72196527cb42369c7cd77de2b4273d9fdc8bd7a254 F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c F ext/recover/recoverclobber.test 294dcc894124ab4ca3a7b35766630742a3d25810fceac22220beb64f70a33a60 F ext/recover/recovercorrupt.test e3f3cbe0162ba681518aac9ea0ae8119f32ac93fb0900b5f09b6318966108e54 F ext/recover/recoverfault.test f3587c218c448545a082b99d59294dff5ec0b7daa15b0556cf926f6c350f221e -F ext/recover/recoverfault2.test 699b3ec07ba6982291e65e1811807f18d7f115234b407c819eaf3529878867f5 +F ext/recover/recoverfault2.test 321036336af23e778a87f148c4cc4407f88fbdab1fd72ddb661669be9020d36b F ext/recover/recoverold.test 46e9d99b595fac583d4c67f74d7d89c20a435c752ef6eeb3e918b599940c88e0 F ext/recover/recoverrowid.test 1694a1a5526d825f71279f3d02ab02a1ee4c5265de18858bf54cb8ec54487ac8 F ext/recover/recoversql.test f9872ff2114e13ffd8ee31e1de06919f62b9b48bc080191b5bd076d10becb60f F ext/recover/sqlite3recover.c 1afcac2bbfcf5ef67a3391f59678dc866348ac88745067e683052ede02c425fb F ext/recover/sqlite3recover.h 81108efb8c4618d3d9c6da4df785212b0e4501aa0d25edfc463405fe839a6640 -F ext/recover/test_recover.c 5941ecf484b6158be26e34c6f7b6c7f03967c72a63db0c08e7fd0fa43023eafa +F ext/recover/test_recover.c 6a6f86ea61d728c67382047d574c62df83e6a28db23c329e93a177093689cd20 F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 F ext/repair/checkfreelist.c e21f06995ff4efdc1622dcceaea4dcba2caa83ca2f31a1607b98a8509168a996 F ext/repair/checkindex.c 4383e4469c21e5b9ae321d0d63cec53e981af9d7a6564be6374f0eeb93dfc890 @@ -1536,7 +1536,7 @@ F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30 F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d1631311a16 F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637 F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc -F test/tester.tcl d759ac44a501fb832f2ea966429ca18acfba0f9a8d34ad5c499332b079b37023 +F test/tester.tcl 65c29b6f1dbf71b0e59a7b221d7e849dfa5a55fa7d0a2902811e8abdecdb1d44 F test/testrunner.tcl 86b57135754ab2160aeb04b4829d321fb285a5cfa7a505fe61d69aed605854cc F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899 F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502 @@ -2011,8 +2011,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 60089547e1fc77ecc02f207ebf75ee3160e5ff25f41d12e02e170fd7fde66602 -R 4e62121f5a47cd1728b6e036c3f3c8c0 +P 9b6b4c7162439a889144edb561356afc66436db921a867c20871f0c556716502 +R 7d7577824671621531c65dae0de3846f U dan -Z 5fc633ab904798afb52a452a7e949f53 +Z b3ef1682460d7df284b8cd64863e6715 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2c3276310d..05a5c951ed 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9b6b4c7162439a889144edb561356afc66436db921a867c20871f0c556716502 \ No newline at end of file +5b05be0861f35804270fbd184ad4b89c23e98cc2fbd56b9e4fe6197daef5fe49 \ No newline at end of file diff --git a/test/tester.tcl b/test/tester.tcl index 5612311dab..9353b91244 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -1548,6 +1548,47 @@ proc explain_i {sql {db db}} { output2 "---- ------------ ------ ------ ------ ---------------- -- -" } +proc execsql_pp {sql {db db}} { + set nCol 0 + $db eval $sql A { + if {$nCol==0} { + set nCol [llength $A(*)] + foreach c $A(*) { + set aWidth($c) [string length $c] + lappend data $c + } + } + foreach c $A(*) { + set n [string length $A($c)] + if {$n > $aWidth($c)} { + set aWidth($c) $n + } + lappend data $A($c) + } + } + if {$nCol>0} { + set nTotal 0 + foreach e [array names aWidth] { incr nTotal $aWidth($e) } + incr nTotal [expr ($nCol-1) * 3] + incr nTotal 4 + + set fmt "" + foreach c $A(*) { + lappend fmt "% -$aWidth($c)s" + } + set fmt "| [join $fmt { | }] |" + + puts [string repeat - $nTotal] + for {set i 0} {$i < [llength $data]} {incr i $nCol} { + set vals [lrange $data $i [expr $i+$nCol-1]] + puts [format $fmt {*}$vals] + if {$i==0} { puts [string repeat - $nTotal] } + } + puts [string repeat - $nTotal] + } +} + + # Show the VDBE program for an SQL statement but omit the Trace # opcode at the beginning. This procedure can be used to prove # that different SQL statements generate exactly the same VDBE code. From 3c24342841614c8f31d0ae27cad111a950959c3e Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 14 Sep 2022 18:57:46 +0000 Subject: [PATCH 104/428] Extra tests and fixes. FossilOrigin-Name: deed5336931b23bb507c064d08e9899b33f04e4f7eee03beb8d7147eb4caa030 --- ext/recover/recover1.test | 12 +++++ ext/recover/recovercorrupt2.test | 76 ++++++++++++++++++++++++++++++++ ext/recover/recoverfault.test | 1 + ext/recover/sqlite3recover.c | 16 +++---- ext/recover/test_recover.c | 14 ++++-- manifest | 19 ++++---- manifest.uuid | 2 +- 7 files changed, 118 insertions(+), 22 deletions(-) create mode 100644 ext/recover/recovercorrupt2.test diff --git a/ext/recover/recover1.test b/ext/recover/recover1.test index 358015a591..79258b7e8f 100644 --- a/ext/recover/recover1.test +++ b/ext/recover/recover1.test @@ -195,7 +195,19 @@ do_execsql_test 11.1 { do_recover_test 11 +do_test 12.1 { + set R [sqlite3_recover_init db "" test.db2] + $R config lostandfound "" + $R config invalid xyz +} {12} +do_test 12.2 { + $R run + $R run +} {21} +do_test 12.3 { + $R finish +} {} finish_test diff --git a/ext/recover/recovercorrupt2.test b/ext/recover/recovercorrupt2.test new file mode 100644 index 0000000000..58dee54815 --- /dev/null +++ b/ext/recover/recovercorrupt2.test @@ -0,0 +1,76 @@ +# 2022 August 28 +# +# 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. +# +#*********************************************************************** +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] recover_common.tcl] +source $testdir/tester.tcl + +set testprefix recovercorrupt2 + +do_execsql_test 1.0 { + PRAGMA page_size = 512; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); + INSERT INTO t1 VALUES(1, 2, 3); + INSERT INTO t1 VALUES(2, hex(randomblob(100)), randomblob(200)); + CREATE INDEX i1 ON t1(b, c); + CREATE TABLE t2(a PRIMARY KEY, b, c) WITHOUT ROWID; + INSERT INTO t2 VALUES(1, 2, 3); + INSERT INTO t2 VALUES(2, hex(randomblob(100)), randomblob(200)); + ANALYZE; + PRAGMA writable_schema = 1; + UPDATE sqlite_schema SET sql = 'CREATE INDEX i1 ON o(world)' WHERE name='i1'; +} + +do_test 1.1 { + set R [sqlite3_recover_init db main test.db2] + $R run + $R finish +} {} + +sqlite3 db2 test.db2 +do_execsql_test -db db2 1.2 { + SELECT sql FROM sqlite_schema +} { + {CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c)} + {CREATE TABLE t2(a PRIMARY KEY, b, c) WITHOUT ROWID} + {CREATE TABLE sqlite_stat1(tbl,idx,stat)} + {CREATE TABLE sqlite_stat4(tbl,idx,neq,nlt,ndlt,sample)} +} +db2 close + +do_execsql_test 1.3 { + PRAGMA writable_schema = 1; + UPDATE sqlite_schema SET sql = 'CREATE TABLE t2 syntax error!' WHERE name='t2'; +} + +do_test 1.4 { + set R [sqlite3_recover_init db main test.db2] + $R run + $R finish +} {} + +sqlite3 db2 test.db2 +do_execsql_test -db db2 1.5 { + SELECT sql FROM sqlite_schema +} { + {CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c)} + {CREATE TABLE sqlite_stat1(tbl,idx,stat)} + {CREATE TABLE sqlite_stat4(tbl,idx,neq,nlt,ndlt,sample)} +} +db2 close + + + +finish_test + diff --git a/ext/recover/recoverfault.test b/ext/recover/recoverfault.test index e6d8392a44..2ea87860b3 100644 --- a/ext/recover/recoverfault.test +++ b/ext/recover/recoverfault.test @@ -72,6 +72,7 @@ do_execsql_test 2.0 { PRAGMA writable_schema = 1; DELETE FROM sqlite_schema WHERE name='t2'; } +faultsim_save_and_close do_faultsim_test 2 -faults oom* -prep { faultsim_restore_and_reopen diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index c67d9b3d49..6c52d99855 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -768,7 +768,7 @@ static int recoverOpenOutput(sqlite3_recover *p){ if( rc==SQLITE_OK ){ sqlite3_backup *pBackup = sqlite3_backup_init(db, "main", db2, "main"); if( pBackup ){ - while( sqlite3_backup_step(pBackup, 1000)==SQLITE_OK ); + sqlite3_backup_step(pBackup, -1); p->errCode = sqlite3_backup_finish(pBackup); }else{ recoverDbError(p, db); @@ -1042,10 +1042,10 @@ static int recoverWriteSchema2(sqlite3_recover *p){ i64 iRoot = sqlite3_column_int64(pSelect, 0); const char *zSql = (const char*)sqlite3_column_text(pSelect, 1); int rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0); - if( rc!=SQLITE_OK && rc!=SQLITE_ERROR ){ - recoverDbError(p, p->dbOut); - }else if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK ){ recoverSqlCallback(p, zSql); + }else if( rc!=SQLITE_ERROR ){ + recoverDbError(p, p->dbOut); } } } @@ -1316,9 +1316,7 @@ static void recoverLostAndFoundPopulate( int iCell = sqlite3_column_int64(pStmt, 2); int iField = sqlite3_column_int64(pStmt, 3); - if( iPrevRoot>0 && ( - iPrevRoot!=iRoot || iPrevPage!=iPage || iPrevCell!=iCell - )){ + if( iPrevRoot>0 && (iPrevPage!=iPage || iPrevCell!=iCell) ){ /* Insert the new row */ sqlite3_bind_int64(pInsert, 1, iPrevRoot); /* rootpgno */ sqlite3_bind_int64(pInsert, 2, iPrevPage); /* pgno */ @@ -1329,7 +1327,7 @@ static void recoverLostAndFoundPopulate( for(ii=0; iixSql ){ + if( sqlite3_step(pInsert)==SQLITE_ROW ){ recoverSqlCallback(p, sqlite3_column_text(pInsert, 0)); } recoverReset(p, pInsert); @@ -1586,7 +1584,7 @@ static int recoverWriteData(sqlite3_recover *p){ sqlite3_bind_int64(pInsert, pTab->iRowidBind, iRowid); } - if( SQLITE_ROW==sqlite3_step(pInsert) && p->xSql ){ + if( SQLITE_ROW==sqlite3_step(pInsert) ){ const char *z = (const char*)sqlite3_column_text(pInsert, 0); recoverSqlCallback(p, z); } diff --git a/ext/recover/test_recover.c b/ext/recover/test_recover.c index 7ab440fa37..d660a5345e 100644 --- a/ext/recover/test_recover.c +++ b/ext/recover/test_recover.c @@ -84,7 +84,7 @@ static int testRecoverCmd( int nArg; const char *zMsg; } aSub[] = { - { "config", 2, "REBASE-BLOB" }, /* 0 */ + { "config", 2, "ARG" }, /* 0 */ { "run", 0, "" }, /* 1 */ { "errmsg", 0, "" }, /* 2 */ { "errcode", 0, "" }, /* 3 */ @@ -115,6 +115,7 @@ static int testRecoverCmd( "lostandfound", /* 1 */ "freelistcorrupt", /* 2 */ "rowids", /* 3 */ + "invalid", /* 4 */ 0 }; int iOp = 0; @@ -128,11 +129,13 @@ static int testRecoverCmd( 789, (void*)Tcl_GetString(objv[3]) /* MAGIC NUMBER! */ ); break; - case 1: + case 1: { + const char *zStr = Tcl_GetString(objv[3]); res = sqlite3_recover_config(pTest->p, - SQLITE_RECOVER_LOST_AND_FOUND, (void*)Tcl_GetString(objv[3]) + SQLITE_RECOVER_LOST_AND_FOUND, (void*)(zStr[0] ? zStr : 0) ); break; + } case 2: { int iVal = 0; if( Tcl_GetBooleanFromObj(interp, objv[3], &iVal) ) return TCL_ERROR; @@ -149,6 +152,10 @@ static int testRecoverCmd( ); break; } + case 4: { + res = sqlite3_recover_config(pTest->p, 12345, 0); + break; + } } Tcl_SetObjResult(interp, Tcl_NewIntObj(res)); break; @@ -210,6 +217,7 @@ static int test_sqlite3_recover_init( } if( getDbPointer(interp, objv[1], &db) ) return TCL_ERROR; zDb = Tcl_GetString(objv[2]); + if( zDb[0]=='\0' ) zDb = 0; pNew = ckalloc(sizeof(TestRecover)); if( bSql==0 ){ diff --git a/manifest b/manifest index b17390fa0e..cc05de1c51 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\srecovery\sof\sutf-16\sdatabases. -D 2022-09-14T16:37:59.285 +C Extra\stests\sand\sfixes. +D 2022-09-14T18:57:46.380 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -387,18 +387,19 @@ F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2 F ext/rbu/sqlite3rbu.c 8737cabdfbee84bb25a7851ecef8b1312be332761238da9be6ddb10c62ad4291 F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812 F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a -F ext/recover/recover1.test 167ad4b267f6db5c06963a72196527cb42369c7cd77de2b4273d9fdc8bd7a254 +F ext/recover/recover1.test 3f26db7692ed98c0de4fc9511f3d3a2f2c91a137b7e6666f3b85b593438b5d09 F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c F ext/recover/recoverclobber.test 294dcc894124ab4ca3a7b35766630742a3d25810fceac22220beb64f70a33a60 F ext/recover/recovercorrupt.test e3f3cbe0162ba681518aac9ea0ae8119f32ac93fb0900b5f09b6318966108e54 -F ext/recover/recoverfault.test f3587c218c448545a082b99d59294dff5ec0b7daa15b0556cf926f6c350f221e +F ext/recover/recovercorrupt2.test 7a1e3fe43231e7571aff4a09f359dcc0c98ef8f74d507c17953d35f2ad9f4c1f +F ext/recover/recoverfault.test 3a0a32b9fc216592b97775d69220695b0926980c0f7424b7a59144e47d7cb568 F ext/recover/recoverfault2.test 321036336af23e778a87f148c4cc4407f88fbdab1fd72ddb661669be9020d36b F ext/recover/recoverold.test 46e9d99b595fac583d4c67f74d7d89c20a435c752ef6eeb3e918b599940c88e0 F ext/recover/recoverrowid.test 1694a1a5526d825f71279f3d02ab02a1ee4c5265de18858bf54cb8ec54487ac8 F ext/recover/recoversql.test f9872ff2114e13ffd8ee31e1de06919f62b9b48bc080191b5bd076d10becb60f -F ext/recover/sqlite3recover.c 1afcac2bbfcf5ef67a3391f59678dc866348ac88745067e683052ede02c425fb +F ext/recover/sqlite3recover.c 29cb0ef3ff22e4a813d741930be123b7a44d59ba4c93868e74c4b0ebbee90a62 F ext/recover/sqlite3recover.h 81108efb8c4618d3d9c6da4df785212b0e4501aa0d25edfc463405fe839a6640 -F ext/recover/test_recover.c 6a6f86ea61d728c67382047d574c62df83e6a28db23c329e93a177093689cd20 +F ext/recover/test_recover.c 72a765616a3fa9dae2ed537d79b00f365d9f639d347858341b71bda7a3a45f56 F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 F ext/repair/checkfreelist.c e21f06995ff4efdc1622dcceaea4dcba2caa83ca2f31a1607b98a8509168a996 F ext/repair/checkindex.c 4383e4469c21e5b9ae321d0d63cec53e981af9d7a6564be6374f0eeb93dfc890 @@ -2011,8 +2012,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 9b6b4c7162439a889144edb561356afc66436db921a867c20871f0c556716502 -R 7d7577824671621531c65dae0de3846f +P 5b05be0861f35804270fbd184ad4b89c23e98cc2fbd56b9e4fe6197daef5fe49 +R 7d672439d169025c8f5a2255e79dee5d U dan -Z b3ef1682460d7df284b8cd64863e6715 +Z 7e26bca497bd6fe58832c34f4ec41a30 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 05a5c951ed..9efa9051fd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5b05be0861f35804270fbd184ad4b89c23e98cc2fbd56b9e4fe6197daef5fe49 \ No newline at end of file +deed5336931b23bb507c064d08e9899b33f04e4f7eee03beb8d7147eb4caa030 \ No newline at end of file From 6fa0a11c7b9e8b8c86454e266e158b79c22f471b Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 14 Sep 2022 20:45:46 +0000 Subject: [PATCH 105/428] Include recovery extension in configure/make builds of testfixture. FossilOrigin-Name: d2f4652144c4a54012a2176c178c5ade3463bd017515f472cfa8dafdde192df3 --- Makefile.in | 6 ++++++ ext/recover/recovercorrupt2.test | 3 +-- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Makefile.in b/Makefile.in index cc4943f2c4..ca3dfdcae8 100644 --- a/Makefile.in +++ b/Makefile.in @@ -433,6 +433,9 @@ TESTSRC = \ $(TOP)/ext/fts3/fts3_term.c \ $(TOP)/ext/fts3/fts3_test.c \ $(TOP)/ext/session/test_session.c \ + $(TOP)/ext/recover/sqlite3recover.c \ + $(TOP)/ext/misc/dbdata.c \ + $(TOP)/ext/recover/test_recover.c \ $(TOP)/ext/rbu/test_rbu.c # Statically linked extensions @@ -1102,6 +1105,9 @@ SHELL_SRC = \ $(TOP)/ext/expert/sqlite3expert.h \ $(TOP)/ext/misc/zipfile.c \ $(TOP)/ext/misc/memtrace.c \ + $(TOP)/ext/misc/dbdata.c \ + $(TOP)/ext/recover/sqlite3recover.c \ + $(TOP)/ext/recover/sqlite3recover.h \ $(TOP)/src/test_windirent.c shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl diff --git a/ext/recover/recovercorrupt2.test b/ext/recover/recovercorrupt2.test index 58dee54815..cb5fa05baa 100644 --- a/ext/recover/recovercorrupt2.test +++ b/ext/recover/recovercorrupt2.test @@ -30,6 +30,7 @@ do_execsql_test 1.0 { ANALYZE; PRAGMA writable_schema = 1; UPDATE sqlite_schema SET sql = 'CREATE INDEX i1 ON o(world)' WHERE name='i1'; + DELETE FROM sqlite_schema WHERE name='sqlite_stat4'; } do_test 1.1 { @@ -45,7 +46,6 @@ do_execsql_test -db db2 1.2 { {CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c)} {CREATE TABLE t2(a PRIMARY KEY, b, c) WITHOUT ROWID} {CREATE TABLE sqlite_stat1(tbl,idx,stat)} - {CREATE TABLE sqlite_stat4(tbl,idx,neq,nlt,ndlt,sample)} } db2 close @@ -66,7 +66,6 @@ do_execsql_test -db db2 1.5 { } { {CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c)} {CREATE TABLE sqlite_stat1(tbl,idx,stat)} - {CREATE TABLE sqlite_stat4(tbl,idx,neq,nlt,ndlt,sample)} } db2 close diff --git a/manifest b/manifest index cc05de1c51..77ef3c20e0 100644 --- a/manifest +++ b/manifest @@ -1,9 +1,9 @@ -C Extra\stests\sand\sfixes. -D 2022-09-14T18:57:46.380 +C Include\srecovery\sextension\sin\sconfigure/make\sbuilds\sof\stestfixture. +D 2022-09-14T20:45:46.778 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in 525bccb89e36a927f9312a231f054a2dc029f6af75901c7fc1a781d51b260323 +F Makefile.in 1dd98f0349f14a7b106ae05904d4d040b4d20f2ab8e2d3c34f8231ba08948e42 F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 F Makefile.msc d547a2fdba38a1c6cd1954977d0b0cc017f5f8fbfbc65287bf8d335808938016 F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e @@ -391,7 +391,7 @@ F ext/recover/recover1.test 3f26db7692ed98c0de4fc9511f3d3a2f2c91a137b7e6666f3b85 F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c F ext/recover/recoverclobber.test 294dcc894124ab4ca3a7b35766630742a3d25810fceac22220beb64f70a33a60 F ext/recover/recovercorrupt.test e3f3cbe0162ba681518aac9ea0ae8119f32ac93fb0900b5f09b6318966108e54 -F ext/recover/recovercorrupt2.test 7a1e3fe43231e7571aff4a09f359dcc0c98ef8f74d507c17953d35f2ad9f4c1f +F ext/recover/recovercorrupt2.test a131d8005337c092e2dfa3b84909ed67ae82d22399a8cfb8c984b2939969ca42 F ext/recover/recoverfault.test 3a0a32b9fc216592b97775d69220695b0926980c0f7424b7a59144e47d7cb568 F ext/recover/recoverfault2.test 321036336af23e778a87f148c4cc4407f88fbdab1fd72ddb661669be9020d36b F ext/recover/recoverold.test 46e9d99b595fac583d4c67f74d7d89c20a435c752ef6eeb3e918b599940c88e0 @@ -2012,8 +2012,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 5b05be0861f35804270fbd184ad4b89c23e98cc2fbd56b9e4fe6197daef5fe49 -R 7d672439d169025c8f5a2255e79dee5d +P deed5336931b23bb507c064d08e9899b33f04e4f7eee03beb8d7147eb4caa030 +R 280ffbfae11c88d50514166a9090fc16 U dan -Z 7e26bca497bd6fe58832c34f4ec41a30 +Z 303ba5cc3bab2dbc239e16c7c361938c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9efa9051fd..bf8054daff 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -deed5336931b23bb507c064d08e9899b33f04e4f7eee03beb8d7147eb4caa030 \ No newline at end of file +d2f4652144c4a54012a2176c178c5ade3463bd017515f472cfa8dafdde192df3 \ No newline at end of file From 409505c74361a5a3c49171f2eb194a3b7a1ef10c Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 15 Sep 2022 02:27:48 +0000 Subject: [PATCH 106/428] Clean up some JS documentation. FossilOrigin-Name: 925fdbfc6c1f9f06951346bc0d4fb81ffb5797d5f1123728931973f6cb1a0efa --- ext/wasm/api/sqlite3-api-oo1.js | 19 +++++++++---------- ext/wasm/kvvfs1.js | 2 +- ext/wasm/testing-worker1-promiser.js | 4 ---- manifest | 16 ++++++++-------- manifest.uuid | 2 +- 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 8280204d62..6a8c229392 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -505,20 +505,19 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ - `.callback` = a function which gets called for each row of the result set, but only if that statement has any result - _rows_. The callback's "this" is the options object. The second - argument passed to the callback is always the current Stmt - object (so that the caller may collect column names, or - similar). The 2nd argument to the callback is always the Stmt - instance, as it's needed if the caller wants to fetch the - column names or some such (noting that they could also be - fetched via `this.columnNames`, if the client provides the - `columnNames` option). + _rows_. The callback's "this" is the options object, noting + that this function synthesizes one if the caller does not pass + one to exec(). The second argument passed to the callback is + always the current Stmt object, as it's needed if the caller + wants to fetch the column names or some such (noting that they + could also be fetched via `this.columnNames`, if the client + provides the `columnNames` option). ACHTUNG: The callback MUST NOT modify the Stmt object. Calling any of the Stmt.get() variants, Stmt.getColumnName(), or similar, is legal, but calling step() or finalize() is - not. Routines which are illegal in this context will trigger an - exception. + not. Member methods which are illegal in this context will + trigger an exception. The first argument passed to the callback defaults to an array of values from the current result row but may be changed with ... diff --git a/ext/wasm/kvvfs1.js b/ext/wasm/kvvfs1.js index fd57051a26..57f990f16b 100644 --- a/ext/wasm/kvvfs1.js +++ b/ext/wasm/kvvfs1.js @@ -105,7 +105,7 @@ log("sqlite3_web_kvvfs_size(",dbStorage,") says", capi.sqlite3_web_kvvfs_size(dbStorage), "bytes"); }); - log("Storage backend:",db.filename /* note that the name was internally translated */); + log("Storage backend:",db.filename); if(0===db.selectValue('select count(*) from sqlite_master')){ log("DB is empty. Use the init button to populate it."); }else{ diff --git a/ext/wasm/testing-worker1-promiser.js b/ext/wasm/testing-worker1-promiser.js index 4cc6547882..a6cbd7910d 100644 --- a/ext/wasm/testing-worker1-promiser.js +++ b/ext/wasm/testing-worker1-promiser.js @@ -145,10 +145,6 @@ mustNotReach ).catch((e)=>{ warn("Intentional error:",e); - // Why does the browser report console.error "Uncaught (in - // promise)" when we catch(), and does so _twice_ if we don't - // catch()? According to all docs, that error must be supressed - // if we explicitly catch(). }); await wtest('exec',{ diff --git a/manifest b/manifest index 6fa5a9fc8a..d06cfdc78b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add/apply\svarious\skvvfs-specific\sutility\sAPIs\sto\sthe\sJS\slayer\sto\sassist\sin\stesting\sand\sanalysis.\sCorrect\sa\sbackwards\sdefault\sarg\scheck\sfor\ssqlite3ApiBootstrap().\sAdd\sexports\sfor\ssqlite3_db_handle(),\ssqlite3_file_control(),\sand\sthe\sSQLITE_FCNTL_xxx\senum. -D 2022-09-13T19:27:03.599 +C Clean\sup\ssome\sJS\sdocumentation. +D 2022-09-15T02:27:48.888 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -483,7 +483,7 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 -F ext/wasm/api/sqlite3-api-oo1.js 4925f4736eb28fd3a6d4dbbd2c078c38be651aa5996041e7f0b81be36506608e +F ext/wasm/api/sqlite3-api-oo1.js 8a8e464c2c786f4faa3fd5b48852818dd2938c3211d82b6176143990a976f8ec F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 F ext/wasm/api/sqlite3-api-prologue.js c496adc0cf2ae427ee900aad01c09db97850bd8b6737f2849cab207c8415b839 F ext/wasm/api/sqlite3-api-worker1.js d33062afa045fd4be01ba4abc266801807472558b862b30056211b00c9c347b4 @@ -508,7 +508,7 @@ F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5 F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19 F ext/wasm/kvvfs.make bfa0aaac384d9f200d2c8e31efb3536b40d139667b88e6eba9c0a71e23da6a5c F ext/wasm/kvvfs1.html 13bb24190bfb276a57b228499519badcc1bf39ed07e4b37bc2a425ce6418fed1 -F ext/wasm/kvvfs1.js a23ee98a78b9da379eab7a20d7ddf5283a684dd7a91685a84686c05216f96689 +F ext/wasm/kvvfs1.js ec1c1d071bb055711f9151df05616111432cf3e6bf7ac7f8dcbcfb56c9d9ed48 F ext/wasm/scratchpad-opfs-main.html 4565cf194e66188190d35f70e82553e2e2d72b9809b73c94ab67b8cfd14d2e0c F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 @@ -524,7 +524,7 @@ F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 -F ext/wasm/testing-worker1-promiser.js c62b5879339eef0b21aebd9d75bc125c86530edc17470afff18077f931cb704a +F ext/wasm/testing-worker1-promiser.js e281c6a58dfe5b455e26997dd2985227588d1b583dc55e1f4f6253bf32f154f5 F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae F ext/wasm/testing1.js 7cd8ab255c238b030d928755ae8e91e7d90a12f2ae601b1b8f7827aaa4fb258e F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 @@ -2024,8 +2024,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 333a45725d1708e0fefa559c33ce1c7eeb425cdb04f594b1f2b48166c1478c79 -R fff963b52d5c3a76677d96f35303ab67 +P 0d78961870ee9f22f1ba16d423377d28dcc36e04b1e31ffd57f3e2fd51f8f0f2 +R bf00de6cf3febea830f05611ed6027b0 U stephan -Z e8c548c1774e1a39daa4183bad155966 +Z 4c6a69dd322d5934ed4c1741f188a00d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e142fcae1f..f903450bae 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0d78961870ee9f22f1ba16d423377d28dcc36e04b1e31ffd57f3e2fd51f8f0f2 \ No newline at end of file +925fdbfc6c1f9f06951346bc0d4fb81ffb5797d5f1123728931973f6cb1a0efa \ No newline at end of file From 0117693f626443b737752dd468eac29415d302c6 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 15 Sep 2022 03:09:00 +0000 Subject: [PATCH 107/428] Split wasmfs-enabled build of sqlite3.js/wasm into sqlite3-wasmfs.js/wasm, as enabling wasmfs breaks all tests/demos which run from a Worker thread. FossilOrigin-Name: 08476f3c218d45846e7496bdae0b06e2122466111fdf2aa2aabb1805b15ef982 --- ext/wasm/GNUmakefile | 35 ++---- ext/wasm/index.html | 4 + ext/wasm/kvvfs.make | 15 +-- ext/wasm/scratchpad-opfs-worker2.js | 2 +- ...-main.html => scratchpad-wasmfs-main.html} | 4 +- ...opfs-main.js => scratchpad-wasmfs-main.js} | 0 ext/wasm/testing-worker1-promiser.js | 1 - ext/wasm/wasmfs.make | 104 ++++++++++++++++++ manifest | 25 +++-- manifest.uuid | 2 +- 10 files changed, 143 insertions(+), 49 deletions(-) rename ext/wasm/{scratchpad-opfs-main.html => scratchpad-wasmfs-main.html} (94%) rename ext/wasm/{scratchpad-opfs-main.js => scratchpad-wasmfs-main.js} (100%) create mode 100644 ext/wasm/wasmfs.make diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index ff37281ea4..3e1bc90248 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -199,22 +199,13 @@ emcc.jsflags += -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory,allocateUTF8OnStack emcc.jsflags += -sUSE_CLOSURE_COMPILER=0 emcc.jsflags += -sIMPORTED_MEMORY emcc.environment := -sENVIRONMENT=web -ENABLE_WASMFS ?= 0 -ifneq (0,$(ENABLE_WASMFS)) - emcc.cflags += -pthread - emcc.jsflags += -pthread -sWASMFS -sPTHREAD_POOL_SIZE=2 - emcc.cflags += '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' - emcc.environment := $(emcc.environment),worker - emcc.jsflags += -sINITIAL_MEMORY=128450560 -else - emcc.jsflags += -sALLOW_MEMORY_GROWTH - # emcc: warning: USE_PTHREADS + ALLOW_MEMORY_GROWTH may run non-wasm code - # slowly, see https://github.com/WebAssembly/design/issues/1271 - # [-Wpthreads-mem-growth] - emcc.jsflags += -sINITIAL_MEMORY=13107200 - #emcc.jsflags += -sINITIAL_MEMORY=64225280 - # ^^^^ 64MB is not enough for WASMFS/OPFS test runs using batch-runner.js -endif +emcc.jsflags += -sALLOW_MEMORY_GROWTH +# emcc: warning: USE_PTHREADS + ALLOW_MEMORY_GROWTH may run non-wasm code +# slowly, see https://github.com/WebAssembly/design/issues/1271 +# [-Wpthreads-mem-growth] +emcc.jsflags += -sINITIAL_MEMORY=13107200 +#emcc.jsflags += -sINITIAL_MEMORY=64225280 +# ^^^^ 64MB is not enough for WASMFS/OPFS test runs using batch-runner.js emcc.jsflags += $(emcc.environment) #emcc.jsflags += -sTOTAL_STACK=4194304 emcc.jsflags += -sEXPORT_NAME=sqlite3InitModule @@ -274,7 +265,6 @@ sqlite3.wasm := sqlite3.wasm sqlite3-wasm.o := $(dir.api)/sqlite3-wasm.o $(sqlite3-wasm.o): emcc.cflags += $(SQLITE_OPT) $(sqlite3-wasm.o): $(dir.top)/sqlite3.c -$(dir.api)/wasm_util.o: emcc.cflags += $(SQLITE_OPT) sqlite3-wasm.c := $(dir.api)/sqlite3-wasm.c jaccwabyt_test.c := $(dir.jacc)/jaccwabyt_test.c # ^^^ FIXME (how?): jaccwabyt_test.c is only needed for the test apps, @@ -282,9 +272,6 @@ jaccwabyt_test.c := $(dir.jacc)/jaccwabyt_test.c # want to test the release builds with those apps, so we cannot simply # elide that file in release builds. That component is critical to the # VFS bindings so needs to be tested along with the core APIs. -ifneq (,$(filter -sWASMFS,$(emcc.jsflags))) - $(sqlite3-wasm.o): emcc.cflags+=-DSQLITE_WASM_OPFS -endif define WASM_C_COMPILE $(1).o := $$(subst .c,.o,$(1)) sqlite3.wasm.obj += $$($(1).o) @@ -293,10 +280,10 @@ $$($(1).o): $$(MAKEFILE) $(1) CLEAN_FILES += $$($(1).o) endef $(foreach c,$(sqlite3-wasm.c) $(jaccwabyt_test.c),$(eval $(call WASM_C_COMPILE,$(c)))) -$(sqlite3.js): $(sqlite3.js): $(MAKEFILE) $(sqlite3.wasm.obj) \ EXPORTED_FUNCTIONS.api \ $(post-js.js) + @echo "Building $@ ..." $(emcc.bin) -o $(sqlite3.js) $(emcc_opt) $(emcc.flags) $(emcc.jsflags) $(sqlite3.wasm.obj) chmod -x $(sqlite3.wasm) ifneq (,$(wasm-strip)) @@ -335,10 +322,6 @@ all: batch ######################################################################## # speedtest1.js... emcc.speedtest1-flags := -g $(emcc_opt) -ifneq (0,$(ENABLE_WASMFS)) - emcc.speedtest1-flags += -pthread -sWASMFS -sPTHREAD_POOL_SIZE=2 - emcc.speedtest1-flags += -DSQLITE_WASM_OPFS -endif emcc.speedtest1-flags += -sINVOKE_RUN=0 #emcc.speedtest1-flags += --no-entry emcc.speedtest1-flags += -flto @@ -365,6 +348,7 @@ $(speedtest1.js): emcc.cflags+= # close enough to the target speed requirements that the 500ms makes a # difference. $(speedtest1.js): $(speedtest1.c) $(sqlite3-wasm.c) $(MAKEFILE) $(sqlite3.c) + @echo "Building $@ ..." $(emcc.bin) \ $(emcc.speedtest1-flags) $(speedtest1.cflags) \ $(SQLITE_OPT) \ @@ -405,3 +389,4 @@ push-fiddle: $(fiddle_files) ######################################################################## include kvvfs.make +include wasmfs.make diff --git a/ext/wasm/index.html b/ext/wasm/index.html index da5ad1a3e9..a7e468aab0 100644 --- a/ext/wasm/index.html +++ b/ext/wasm/index.html @@ -42,6 +42,10 @@
  • speedtest1-kvvfs: a variant of speedtest1 built solely for the kv-vfs feature.
  • kvvfs1: very basic demo of using the key-value vfs for storing a persistent db in JS localStorage or sessionStorage.
  • +
  • scratchpad-wasmfs-main: + experimenting with WASMFS/OPFS-based persistence. Maintenance + reminder: we cannot currently (2022-09-15) load WASMFS in a + worker due to an Emscripten limitation.
  • diff --git a/ext/wasm/kvvfs.make b/ext/wasm/kvvfs.make index ad743dfda6..3ef9dbddf9 100644 --- a/ext/wasm/kvvfs.make +++ b/ext/wasm/kvvfs.make @@ -83,9 +83,10 @@ endif $(kvvfs.js): $(kvvfs.wasm.c) $(sqlite3.c) $(kvvfs.extra.c) \ EXPORTED_FUNCTIONS.api $(MAKEFILE) $(MAKEFILE.kvvfs) \ $(post-js.js) + @echo "Building $@ ..." $(emcc.bin) -o $@ $(emcc_opt) $(emcc.flags) \ - $(SQLITE_OPT) \ - $(kvvfs.cflags) $(kvvfs.jsflags) $(kvvfs.wasm.c) $(kvvfs.extra.c) + $(SQLITE_OPT) \ + $(kvvfs.cflags) $(kvvfs.jsflags) $(kvvfs.wasm.c) $(kvvfs.extra.c) chmod -x $(kvvfs.wasm) ifneq (,$(wasm-strip)) $(wasm-strip) $(kvvfs.wasm) @@ -102,11 +103,11 @@ speedtest1-kvvfs.wasm := speedtest1-kvvfs.wasm CLEAN_FILES += $(speedtest1-kvvfs.js) $(speedtest1-kvvfs.wasm) $(speedtest1-kvvfs.js): $(speedtest1.c) $(sqlite3-wasm.c) $(sqlite3.c) $(MAKEFILE.kvvfs) $(emcc.bin) \ - $(emcc.speedtest1-flags) $(speedtest1.cflags) \ - $(SQLITE_OPT) \ - -sEXIT_RUNTIME=1 \ - $(kvvfs.cflags) \ - -o $@ $(speedtest1.c) $(sqlite3-wasm.c) -lm + $(emcc.speedtest1-flags) $(speedtest1.cflags) \ + $(SQLITE_OPT) \ + -sEXIT_RUNTIME=1 \ + $(kvvfs.cflags) \ + -o $@ $(speedtest1.c) $(sqlite3-wasm.c) -lm ifneq (,$(wasm-strip)) $(wasm-strip) $(speedtest1-kvvfs.wasm) endif diff --git a/ext/wasm/scratchpad-opfs-worker2.js b/ext/wasm/scratchpad-opfs-worker2.js index e27fe2b372..e3eba16a2e 100644 --- a/ext/wasm/scratchpad-opfs-worker2.js +++ b/ext/wasm/scratchpad-opfs-worker2.js @@ -18,7 +18,7 @@ 'use strict'; (function(){ const toss = function(...args){throw new Error(args.join(' '))}; - importScripts('sqlite3.js'); + importScripts('sqlite3-wasmfs.js'); /** Posts a message in the form {type,data} unless passed more than 2 diff --git a/ext/wasm/scratchpad-opfs-main.html b/ext/wasm/scratchpad-wasmfs-main.html similarity index 94% rename from ext/wasm/scratchpad-opfs-main.html rename to ext/wasm/scratchpad-wasmfs-main.html index 8edd15a671..91f61526cd 100644 --- a/ext/wasm/scratchpad-opfs-main.html +++ b/ext/wasm/scratchpad-wasmfs-main.html @@ -33,8 +33,8 @@

    All stuff on this page happens in the dev console.


    - + - + diff --git a/ext/wasm/scratchpad-opfs-main.js b/ext/wasm/scratchpad-wasmfs-main.js similarity index 100% rename from ext/wasm/scratchpad-opfs-main.js rename to ext/wasm/scratchpad-wasmfs-main.js diff --git a/ext/wasm/testing-worker1-promiser.js b/ext/wasm/testing-worker1-promiser.js index a6cbd7910d..1f2347c6fe 100644 --- a/ext/wasm/testing-worker1-promiser.js +++ b/ext/wasm/testing-worker1-promiser.js @@ -150,7 +150,6 @@ await wtest('exec',{ sql:'select 1 union all select 3', resultRows: [], - //rowMode: 'array', // array is the default in the Worker interface }, function(ev){ ev = ev.result; T.assert(2 === ev.resultRows.length) diff --git a/ext/wasm/wasmfs.make b/ext/wasm/wasmfs.make new file mode 100644 index 0000000000..d96e68f77d --- /dev/null +++ b/ext/wasm/wasmfs.make @@ -0,0 +1,104 @@ +#!/usr/bin/make +#^^^^ help emacs select makefile mode +# +# This is a sub-make for building a standalone wasmfs-based +# sqlite3.wasm. It is intended to be "include"d from the main +# GNUMakefile. +# +# Notable potential TODOs: +# +# - Trim down a custom sqlite3-api.js for this build. We can elimate +# the jaccwabyt dependency, for example, because this build won't +# make use of the VFS bits. Similarly, we can eliminate or replace +# parts of the OO1 API, or provide a related API which manages +# singletons of the localStorage/sessionStorage instances. +# +######################################################################## +MAKEFILE.wasmfs := $(lastword $(MAKEFILE_LIST)) + +wasmfs.js := sqlite3-wasmfs.js +wasmfs.wasm := sqlite3-wasmfs.wasm +wasmfs.wasm.c := $(dir.api)/sqlite3-wasm.c + +CLEAN_FILES += $(wasmfs.js) $(wasmfs.wasm) + +######################################################################## +# emcc flags for .c/.o/.wasm. +wasmfs.flags = +#wasmfs.flags += -v # _very_ loud but also informative about what it's doing + +######################################################################## +# emcc flags for .c/.o. +wasmfs.cflags := +wasmfs.cflags += -std=c99 -fPIC -g +wasmfs.cflags += -pthread +wasmfs.cflags += -I. -I$(dir.top) +wasmfs.cflags += $(SQLITE_OPT) -DSQLITE_WASM_OPFS +wasmfs.cflags += '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' + +wasmfs.extra.c := +ifeq (1,1) + # To get testing1.js to run with $(wasmfs.js) we need... + wasmfs.extra.c += $(jaccwabyt_test.c) +endif + +######################################################################## +# emcc flags specific to building the final .js/.wasm file... +wasmfs.jsflags := -fPIC +wasmfs.jsflags += --no-entry +wasmfs.jsflags += --minify 0 +wasmfs.jsflags += -sENVIRONMENT=web,worker +wasmfs.jsflags += -sMODULARIZE +wasmfs.jsflags += -sSTRICT_JS +wasmfs.jsflags += -sDYNAMIC_EXECUTION=0 +wasmfs.jsflags += -sNO_POLYFILL +ifeq (,$(wasmfs.extra.c)) + wasmfs.jsflags += -sEXPORTED_FUNCTIONS=@$(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api +else + # need more exports for jaccwabyt test code... + wasmfs.jsflags += -sEXPORTED_FUNCTIONS=@$(dir.wasm)/EXPORTED_FUNCTIONS.api +endif +wasmfs.jsflags += -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory,allocateUTF8OnStack + # wasmMemory ==> for -sIMPORTED_MEMORY + # allocateUTF8OnStack ==> wasmfs internals +wasmfs.jsflags += -sUSE_CLOSURE_COMPILER=0 +wasmfs.jsflags += -sIMPORTED_MEMORY +#wasmfs.jsflags += -sINITIAL_MEMORY=13107200 +#wasmfs.jsflags += -sTOTAL_STACK=4194304 +wasmfs.jsflags += -sEXPORT_NAME=sqlite3InitModule +wasmfs.jsflags += -sGLOBAL_BASE=4096 # HYPOTHETICALLY keep func table indexes from overlapping w/ heap addr. +wasmfs.jsflags += --post-js=$(post-js.js) +#wasmfs.jsflags += -sFILESYSTEM=0 # only for experimentation. sqlite3 needs the FS API +# Perhaps the wasmfs build doesn't? +#wasmfs.jsflags += -sABORTING_MALLOC +wasmfs.jsflags += -sALLOW_TABLE_GROWTH +wasmfs.jsflags += -Wno-limited-postlink-optimizations +# ^^^^^ it likes to warn when we have "limited optimizations" via the -g3 flag. +wasmfs.jsflags += -sERROR_ON_UNDEFINED_SYMBOLS=0 +wasmfs.jsflags += -sLLD_REPORT_UNDEFINED +#wasmfs.jsflags += --import-undefined +wasmfs.jsflags += -sMEMORY64=0 +wasmfs.jsflags += -pthread -sWASMFS -sPTHREAD_POOL_SIZE=2 +wasmfs.jsflags += -sINITIAL_MEMORY=128450560 +#wasmfs.jsflags += -sALLOW_MEMORY_GROWTH +#^^^ using ALLOW_MEMORY_GROWTH produces a warning from emcc: +# USE_PTHREADS + ALLOW_MEMORY_GROWTH may run non-wasm code slowly, +# see https://github.com/WebAssembly/design/issues/1271 [-Wpthreads-mem-growth] +ifneq (0,$(enable_bigint)) +wasmfs.jsflags += -sWASM_BIGINT +endif + +$(wasmfs.js): $(wasmfs.wasm.c) $(sqlite3.c) $(wasmfs.extra.c) \ + EXPORTED_FUNCTIONS.api $(MAKEFILE) $(MAKEFILE.wasmfs) \ + $(post-js.js) + @echo "Building $@ ..." + $(emcc.bin) -o $@ $(emcc_opt) $(emcc.flags) \ + $(wasmfs.cflags) $(wasmfs.jsflags) $(wasmfs.wasm.c) $(wasmfs.extra.c) + chmod -x $(wasmfs.wasm) +ifneq (,$(wasm-strip)) + $(wasm-strip) $(wasmfs.wasm) +endif + @ls -la $@ $(wasmfs.wasm) + +wasmfs: $(wasmfs.js) +all: wasmfs diff --git a/manifest b/manifest index d06cfdc78b..045ad7cfc0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Clean\sup\ssome\sJS\sdocumentation. -D 2022-09-15T02:27:48.888 +C Split\swasmfs-enabled\sbuild\sof\ssqlite3.js/wasm\sinto\ssqlite3-wasmfs.js/wasm,\sas\senabling\swasmfs\sbreaks\sall\stests/demos\swhich\srun\sfrom\sa\sWorker\sthread. +D 2022-09-15T03:09:00.109 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile b175a00599c976fe9e7bc02bbc1337b5e3b81d042320c3a0be1621d2c7a21b21 +F ext/wasm/GNUmakefile f06963ecf1a8275755a568f84ad66e1c6a093e190cffd84f1414d8e75d3c5277 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 150a793a47205b8009ac934f3b6d6ebf67b965c072339aaa25ce808a19e116cc F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -501,19 +501,19 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 -F ext/wasm/index.html f4b04f866ac420e7037b3818e9cb73ccc059675e0f24482932a2f7a121b814e3 +F ext/wasm/index.html 97968cdfa957c9ede7c81df55c9a2276b1a9784dc41a9802d3d94ad9c30ba2ed F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19 -F ext/wasm/kvvfs.make bfa0aaac384d9f200d2c8e31efb3536b40d139667b88e6eba9c0a71e23da6a5c +F ext/wasm/kvvfs.make 99d61a438232d03f7a39fff05a5738d00d5af629e54b3f03caf87f041c194d51 F ext/wasm/kvvfs1.html 13bb24190bfb276a57b228499519badcc1bf39ed07e4b37bc2a425ce6418fed1 F ext/wasm/kvvfs1.js ec1c1d071bb055711f9151df05616111432cf3e6bf7ac7f8dcbcfb56c9d9ed48 -F ext/wasm/scratchpad-opfs-main.html 4565cf194e66188190d35f70e82553e2e2d72b9809b73c94ab67b8cfd14d2e0c -F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 -F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d1694043eaaf41ae18dbfef2e48c0 +F ext/wasm/scratchpad-opfs-worker2.js 779500d97140812509c04ba182de5bf76727052bdcbc09b3cedfb71a8574e07b +F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06 w ext/wasm/scratchpad-opfs-main.html +F ext/wasm/scratchpad-wasmfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e w ext/wasm/scratchpad-opfs-main.js F ext/wasm/speedtest1-kvvfs.html fe7d314343b275f8b241af83267ca56274ea995535c6abca4d6e568a98fa31e3 F ext/wasm/speedtest1-worker.html 6b5fda04d0b69e8c2651689356cb0c28fd33aa1a82b03dcbc8b0d68fbd7ed57f F ext/wasm/speedtest1-worker.js 356b9953add4449acf199793db9b76b11ee016021918d8daffd19f08ec68d305 @@ -524,11 +524,12 @@ F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 -F ext/wasm/testing-worker1-promiser.js e281c6a58dfe5b455e26997dd2985227588d1b583dc55e1f4f6253bf32f154f5 +F ext/wasm/testing-worker1-promiser.js 63448fddfd3b8c89ff667d17c8b31c6c2259dd4647ebbbd28f3a921c48e924da F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae F ext/wasm/testing1.js 7cd8ab255c238b030d928755ae8e91e7d90a12f2ae601b1b8f7827aaa4fb258e F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c +F ext/wasm/wasmfs.make 86ce03d75a462ef4431e0c2d851f707156975bca5347daa8d8a1cb430ba83a62 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2024,8 +2025,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 0d78961870ee9f22f1ba16d423377d28dcc36e04b1e31ffd57f3e2fd51f8f0f2 -R bf00de6cf3febea830f05611ed6027b0 +P 925fdbfc6c1f9f06951346bc0d4fb81ffb5797d5f1123728931973f6cb1a0efa +R a69df33d531c68d33c70b5a00ef5df51 U stephan -Z 4c6a69dd322d5934ed4c1741f188a00d +Z bd73fd5e630d2b9c280c487da7e4705d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f903450bae..1f14dcc292 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -925fdbfc6c1f9f06951346bc0d4fb81ffb5797d5f1123728931973f6cb1a0efa \ No newline at end of file +08476f3c218d45846e7496bdae0b06e2122466111fdf2aa2aabb1805b15ef982 \ No newline at end of file From dae7518ae407ab379068cd3685e773b462d3b61a Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 15 Sep 2022 03:16:49 +0000 Subject: [PATCH 108/428] Remove some stale comments. Get scratchpad-opfs-worker2.js running again in prep for reuse in experimenting with the OPFS all-synchronous API. FossilOrigin-Name: 4cbbd370186f84e440f496e7f11c641d7dc1723acc46e4a31483392e0eb046e9 --- ext/wasm/index.html | 2 ++ ext/wasm/scratchpad-opfs-worker.html | 25 ++----------------------- ext/wasm/scratchpad-opfs-worker2.js | 4 ++-- ext/wasm/wasmfs.make | 9 --------- manifest | 22 +++++++++++----------- manifest.uuid | 2 +- 6 files changed, 18 insertions(+), 46 deletions(-) diff --git a/ext/wasm/index.html b/ext/wasm/index.html index a7e468aab0..4b21839a4a 100644 --- a/ext/wasm/index.html +++ b/ext/wasm/index.html @@ -46,6 +46,8 @@ experimenting with WASMFS/OPFS-based persistence. Maintenance reminder: we cannot currently (2022-09-15) load WASMFS in a worker due to an Emscripten limitation. +
  • scratchpad-opfs-worker: + experimenting with OPFS from a Worker thread (without WASMFS). diff --git a/ext/wasm/scratchpad-opfs-worker.html b/ext/wasm/scratchpad-opfs-worker.html index 2f3e5b1de6..0ab7822d82 100644 --- a/ext/wasm/scratchpad-opfs-worker.html +++ b/ext/wasm/scratchpad-opfs-worker.html @@ -6,31 +6,10 @@ - sqlite3 WASMFS/OPFS Worker-thread Scratchpad + sqlite3 OPFS Worker-thread Scratchpad -
    sqlite3 WASMFS/OPFS Worker-thread Scratchpad
    - - -

    This test is known, as of 2022-08-13, to not work.

    -

    Scratchpad/test app for the WASMF/OPFS integration in a - WORKER thread. This page requires that the sqlite3 API have - been built with WASMFS support. If OPFS support is available then - it "should" persist a database across reloads (watch the dev console - output), otherwise it will not. -

    +
    sqlite3 OPFS Worker-thread Scratchpad

    All stuff on this page happens in the dev console.


    diff --git a/ext/wasm/scratchpad-opfs-worker2.js b/ext/wasm/scratchpad-opfs-worker2.js index e3eba16a2e..04b6bfc274 100644 --- a/ext/wasm/scratchpad-opfs-worker2.js +++ b/ext/wasm/scratchpad-opfs-worker2.js @@ -18,7 +18,7 @@ 'use strict'; (function(){ const toss = function(...args){throw new Error(args.join(' '))}; - importScripts('sqlite3-wasmfs.js'); + importScripts('sqlite3.js'); /** Posts a message in the form {type,data} unless passed more than 2 @@ -37,7 +37,7 @@ const stderr = console.error.bind(console);//function(...args){wMsg('stderr', args);}; const test1 = function(db){ - db.execMulti("create table if not exists t(a);") + db.exec("create table if not exists t(a);") .transaction(function(db){ db.prepare("insert into t(a) values(?)") .bind(new Date().getTime()) diff --git a/ext/wasm/wasmfs.make b/ext/wasm/wasmfs.make index d96e68f77d..f69da43b9e 100644 --- a/ext/wasm/wasmfs.make +++ b/ext/wasm/wasmfs.make @@ -4,15 +4,6 @@ # This is a sub-make for building a standalone wasmfs-based # sqlite3.wasm. It is intended to be "include"d from the main # GNUMakefile. -# -# Notable potential TODOs: -# -# - Trim down a custom sqlite3-api.js for this build. We can elimate -# the jaccwabyt dependency, for example, because this build won't -# make use of the VFS bits. Similarly, we can eliminate or replace -# parts of the OO1 API, or provide a related API which manages -# singletons of the localStorage/sessionStorage instances. -# ######################################################################## MAKEFILE.wasmfs := $(lastword $(MAKEFILE_LIST)) diff --git a/manifest b/manifest index 045ad7cfc0..543bbdbf9a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Split\swasmfs-enabled\sbuild\sof\ssqlite3.js/wasm\sinto\ssqlite3-wasmfs.js/wasm,\sas\senabling\swasmfs\sbreaks\sall\stests/demos\swhich\srun\sfrom\sa\sWorker\sthread. -D 2022-09-15T03:09:00.109 +C Remove\ssome\sstale\scomments.\sGet\sscratchpad-opfs-worker2.js\srunning\sagain\sin\sprep\sfor\sreuse\sin\sexperimenting\swith\sthe\sOPFS\sall-synchronous\sAPI. +D 2022-09-15T03:16:49.402 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -501,7 +501,7 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 -F ext/wasm/index.html 97968cdfa957c9ede7c81df55c9a2276b1a9784dc41a9802d3d94ad9c30ba2ed +F ext/wasm/index.html 7e38070c8c64738eb3893be19e1291d66629fc424f1a70e12c4b459d51dbeffe F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f @@ -509,11 +509,11 @@ F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e5 F ext/wasm/kvvfs.make 99d61a438232d03f7a39fff05a5738d00d5af629e54b3f03caf87f041c194d51 F ext/wasm/kvvfs1.html 13bb24190bfb276a57b228499519badcc1bf39ed07e4b37bc2a425ce6418fed1 F ext/wasm/kvvfs1.js ec1c1d071bb055711f9151df05616111432cf3e6bf7ac7f8dcbcfb56c9d9ed48 -F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 +F ext/wasm/scratchpad-opfs-worker.html 5fdda167571264300f388847d34f00b77dd48984a8dba2ee9c099c3ffa05db66 F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 -F ext/wasm/scratchpad-opfs-worker2.js 779500d97140812509c04ba182de5bf76727052bdcbc09b3cedfb71a8574e07b -F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06 w ext/wasm/scratchpad-opfs-main.html -F ext/wasm/scratchpad-wasmfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e w ext/wasm/scratchpad-opfs-main.js +F ext/wasm/scratchpad-opfs-worker2.js 8cd4961e1c96fbaad9743a34cd17d0d27b9286b845e77242d444a4c78fe2a810 +F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06 +F ext/wasm/scratchpad-wasmfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e F ext/wasm/speedtest1-kvvfs.html fe7d314343b275f8b241af83267ca56274ea995535c6abca4d6e568a98fa31e3 F ext/wasm/speedtest1-worker.html 6b5fda04d0b69e8c2651689356cb0c28fd33aa1a82b03dcbc8b0d68fbd7ed57f F ext/wasm/speedtest1-worker.js 356b9953add4449acf199793db9b76b11ee016021918d8daffd19f08ec68d305 @@ -529,7 +529,7 @@ F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d766 F ext/wasm/testing1.js 7cd8ab255c238b030d928755ae8e91e7d90a12f2ae601b1b8f7827aaa4fb258e F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c -F ext/wasm/wasmfs.make 86ce03d75a462ef4431e0c2d851f707156975bca5347daa8d8a1cb430ba83a62 +F ext/wasm/wasmfs.make 44c35734f531199dab7c49f82ed265344c86aa03d741fd7911ddb4de0c551ddd F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2025,8 +2025,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 925fdbfc6c1f9f06951346bc0d4fb81ffb5797d5f1123728931973f6cb1a0efa -R a69df33d531c68d33c70b5a00ef5df51 +P 08476f3c218d45846e7496bdae0b06e2122466111fdf2aa2aabb1805b15ef982 +R 520f42f1cc70efba1aa8d99f68fa03bf U stephan -Z bd73fd5e630d2b9c280c487da7e4705d +Z a50915abdf47f7036b78a2ce4e5a53d8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1f14dcc292..d3cb0d93c3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -08476f3c218d45846e7496bdae0b06e2122466111fdf2aa2aabb1805b15ef982 \ No newline at end of file +4cbbd370186f84e440f496e7f11c641d7dc1723acc46e4a31483392e0eb046e9 \ No newline at end of file From 73390f09c2e93c61e4da507d2b61e4cf03a98b59 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 15 Sep 2022 03:54:52 +0000 Subject: [PATCH 109/428] Fix windows builds of recover extension. FossilOrigin-Name: abcbb6abfe08fc590123f0aa1bd72cfdecc75908f078f1348dc0e957de98bf52 --- Makefile.msc | 6 ++++++ ext/recover/sqlite3recover.c | 18 +++++++----------- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/Makefile.msc b/Makefile.msc index 9baa4ae937..4abf2d8e82 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -1587,6 +1587,9 @@ TESTEXT = \ $(TOP)\ext\misc\unionvtab.c \ $(TOP)\ext\misc\wholenumber.c \ $(TOP)\ext\rtree\test_rtreedoc.c \ + $(TOP)\ext\recover\sqlite3recover.c \ + $(TOP)\ext\recover\test_recover.c \ + $(TOP)\ext\misc\dbdata.c \ fts5.c # If use of zlib is enabled, add the "zipfile.c" source file. @@ -2224,6 +2227,9 @@ SHELL_SRC = \ $(TOP)\ext\expert\sqlite3expert.c \ $(TOP)\ext\expert\sqlite3expert.h \ $(TOP)\ext\misc\memtrace.c \ + $(TOP)/ext/misc/dbdata.c \ + $(TOP)/ext/recover/sqlite3recover.c \ + $(TOP)/ext/recover/sqlite3recover.h \ $(TOP)\src\test_windirent.c # If use of zlib is enabled, add the "zipfile.c" source file. diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index 6c52d99855..a370ca0c46 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -137,7 +137,7 @@ struct RecoverColumn { typedef struct RecoverBitmap RecoverBitmap; struct RecoverBitmap { i64 nPg; /* Size of bitmap */ - u32 aElem[0]; /* Array of 32-bit bitmasks */ + u32 aElem[1]; /* Array of 32-bit bitmasks */ }; /* @@ -999,8 +999,8 @@ static int recoverWriteSchema1(sqlite3_recover *p){ recoverSqlCallback(p, zSql); if( bTable && !bVirtual ){ if( SQLITE_ROW==sqlite3_step(pTblname) ){ - const char *zName = sqlite3_column_text(pTblname, 0); - recoverAddTable(p, zName, iRoot); + const char *zTbl = (const char*)sqlite3_column_text(pTblname, 0); + recoverAddTable(p, zTbl, iRoot); } recoverReset(p, pTblname); } @@ -1252,7 +1252,6 @@ static sqlite3_stmt *recoverLostAndFoundInsert( int nTotal = nField + 4; int ii; char *zBind = 0; - const char *zSep = ""; sqlite3_stmt *pRet = 0; if( p->xSql==0 ){ @@ -1328,7 +1327,7 @@ static void recoverLostAndFoundPopulate( recoverBindValue(p, pInsert, 5+ii, apVal[ii]); } if( sqlite3_step(pInsert)==SQLITE_ROW ){ - recoverSqlCallback(p, sqlite3_column_text(pInsert, 0)); + recoverSqlCallback(p, (const char*)sqlite3_column_text(pInsert, 0)); } recoverReset(p, pInsert); @@ -1376,7 +1375,7 @@ static void recoverLostAndFoundPopulate( ** db and writes all orphaned rows to it. Or, if the recover handle is ** in SQL callback mode, issues equivalent callbacks. */ -static int recoverLostAndFound(sqlite3_recover *p){ +static void recoverLostAndFound(sqlite3_recover *p){ i64 nPg = 0; RecoverBitmap *pMap = 0; @@ -1558,9 +1557,7 @@ static int recoverWriteData(sqlite3_recover *p){ if( bNewCell ){ if( nVal>=0 ){ - int ii; int iVal = 0; - int iBind = 1; if( pInsert==0 || nVal!=nInsert ){ recoverFinalize(p, pInsert); @@ -1570,9 +1567,8 @@ static int recoverWriteData(sqlite3_recover *p){ if( nVal>0 ){ for(ii=0; iinCol; ii++){ RecoverColumn *pCol = &pTab->aCol[ii]; - - if( pCol->iBind>0 ){ - int iBind = pCol->iBind; + int iBind = pCol->iBind; + if( iBind>0 ){ if( pCol->bIPK ){ sqlite3_bind_int64(pInsert, iBind, iRowid); }else if( pCol->iField Date: Thu, 15 Sep 2022 06:42:41 +0000 Subject: [PATCH 110/428] More work on the synchronous OPFS experimentation. Numerous wasm/js build tweaks. Add speeedtest-wasmfs.html, the wasmfs/opfs counterpart of speedtest1.html. FossilOrigin-Name: 00ee49a3a2c148480f614e49a0ee5b35a255758c0a53693f0b464b31e7a4045b --- ext/wasm/EXPORTED_FUNCTIONS.fiddle | 2 - ext/wasm/GNUmakefile | 79 +++-- ext/wasm/api/sqlite3-api-oo1.js | 11 +- ext/wasm/api/sqlite3-api-prologue.js | 4 +- ext/wasm/api/sqlite3-wasm.c | 10 +- ext/wasm/index.html | 1 + ext/wasm/kvvfs.make | 10 +- ext/wasm/scratchpad-opfs-worker.js | 23 +- ext/wasm/scratchpad-opfs-worker2.js | 445 +++++++++++++++++++++++++-- ext/wasm/speedtest1-wasmfs.html | 159 ++++++++++ ext/wasm/speedtest1-worker.html | 16 +- ext/wasm/speedtest1-worker.js | 9 +- ext/wasm/speedtest1.html | 8 +- ext/wasm/wasmfs.make | 133 ++++---- manifest | 37 +-- manifest.uuid | 2 +- 16 files changed, 792 insertions(+), 157 deletions(-) create mode 100644 ext/wasm/speedtest1-wasmfs.html diff --git a/ext/wasm/EXPORTED_FUNCTIONS.fiddle b/ext/wasm/EXPORTED_FUNCTIONS.fiddle index 602d612548..b96ce4e67c 100644 --- a/ext/wasm/EXPORTED_FUNCTIONS.fiddle +++ b/ext/wasm/EXPORTED_FUNCTIONS.fiddle @@ -5,5 +5,3 @@ _fiddle_the_db _fiddle_db_arg _fiddle_db_filename _fiddle_reset_db -_sqlite3_wasm_init_opfs -_sqlite3_wasm_vfs_unlink diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 3e1bc90248..390472dea2 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -9,10 +9,11 @@ # components). SHELL := $(shell which bash 2>/dev/null) all: +emcc_opt ?= -O0 .PHONY: fiddle ifneq (,$(wildcard /home/stephan)) - fiddle_opt ?= -O0 + fiddle_opt ?= $(emcc_opt) else fiddle_opt = -Os endif @@ -61,7 +62,6 @@ SQLITE_OPT = \ # SQLITE_OMIT_LOAD_EXTENSION: if this is true, sqlite3_vfs::xDlOpen # and friends may be NULL. -emcc_opt ?= -O0 .PHONY: release release: $(MAKE) 'emcc_opt=-Os -g3 -flto' @@ -101,12 +101,12 @@ wasm-strip ?= $(shell which wasm-strip 2>/dev/null) ifeq (,$(filter clean,$(MAKECMDGOALS))) ifeq (,$(wasm-strip)) $(info WARNING: *******************************************************************) - $(info WARNING: builds using -O3/-Os/-Oz will minify WASM-exported names,) + $(info WARNING: builds using -O2/-O3/-Os/-Oz will minify WASM-exported names,) $(info WARNING: breaking _All The Things_. The workaround for that is to build) $(info WARNING: with -g3 (which explodes the file size) and then strip the debug) $(info WARNING: info after compilation, using wasm-strip, to shrink the wasm file.) $(info WARNING: wasm-strip was not found in the PATH so we cannot strip those.) - $(info WARNING: If this build uses any optimization level higher than -O2 then) + $(info WARNING: If this build uses any optimization level higher than -O1 then) $(info WARNING: the ***resulting WASM binary WILL NOT BE USABLE***.) $(info WARNING: wasm-strip is part of the wabt package:) $(info WARNING: https://github.com/WebAssembly/wabt) @@ -116,6 +116,13 @@ ifeq (,$(wasm-strip)) endif endif # 'make clean' check + +ifeq (,$(wasm-strip)) + maybe-wasm-strip = echo "not wasm-stripping" +else + maybe-wasm-strip = $(wasm-strip) +endif + ifeq (release,$(filter release,$(MAKECMDGOALS))) ifeq (,$(wasm-strip)) $(error Cannot make release-quality binary because wasm-strip is not available. \ @@ -286,9 +293,7 @@ $(sqlite3.js): $(MAKEFILE) $(sqlite3.wasm.obj) \ @echo "Building $@ ..." $(emcc.bin) -o $(sqlite3.js) $(emcc_opt) $(emcc.flags) $(emcc.jsflags) $(sqlite3.wasm.obj) chmod -x $(sqlite3.wasm) -ifneq (,$(wasm-strip)) - $(wasm-strip) $(sqlite3.wasm) -endif + $(maybe-wasm-strip) $(sqlite3.wasm) @ls -la $@ $(sqlite3.wasm) CLEAN_FILES += $(sqlite3.js) $(sqlite3.wasm) @@ -321,25 +326,28 @@ all: batch # end batch-runner.js ######################################################################## # speedtest1.js... -emcc.speedtest1-flags := -g $(emcc_opt) -emcc.speedtest1-flags += -sINVOKE_RUN=0 -#emcc.speedtest1-flags += --no-entry -emcc.speedtest1-flags += -flto -emcc.speedtest1-flags += -sABORTING_MALLOC -emcc.speedtest1-flags += -sINITIAL_MEMORY=128450560 -emcc.speedtest1-flags += -sSTRICT_JS -emcc.speedtest1-flags += $(emcc.environment) -emcc.speedtest1-flags += -sMODULARIZE -emcc.speedtest1-flags += -sEXPORT_NAME=sqlite3Speedtest1InitModule -emcc.speedtest1-flags += -Wno-limited-postlink-optimizations -emcc.speedtest1-flags += -sEXPORTED_FUNCTIONS=_main,_malloc,_free,_sqlite3_wasm_vfs_unlink,_sqlite3_wasm_init_opfs -emcc.speedtest1-flags += -sDYNAMIC_EXECUTION=0 -emcc.speedtest1-flags += --minify 0 +# speedtest1-common.eflags = emcc flags used by multiple builds of speedtest1 +# speedtest1.eflags = emcc flags used by main build of speedtest1 +speedtest1-common.eflags := -g $(emcc_opt) +speedtest1.eflags := +speedtest1-common.eflags += -sINVOKE_RUN=0 +#speedtest1-common.eflags += --no-entry +speedtest1-common.eflags += -flto +speedtest1-common.eflags += -sABORTING_MALLOC +speedtest1-common.eflags += -sINITIAL_MEMORY=128450560 +speedtest1-common.eflags += -sSTRICT_JS +speedtest1-common.eflags += -sMODULARIZE +speedtest1-common.eflags += -Wno-limited-postlink-optimizations +speedtest1-common.eflags += -sEXPORTED_FUNCTIONS=_main,_malloc,_free +speedtest1-common.eflags += -sDYNAMIC_EXECUTION=0 +speedtest1-common.eflags += --minify 0 +speedtest1-common.eflags += -sEXPORT_NAME=sqlite3Speedtest1InitModule +speedtest1.eflags += -sENVIRONMENT=web speedtest1.js := speedtest1.js speedtest1.wasm := $(subst .js,.wasm,$(speedtest1.js)) speedtest1.cflags := \ - -I. -I$(dir.top) \ + -I. -I.. -I$(dir.top) \ -DSQLITE_SPEEDTEST1_WASM $(speedtest1.js): emcc.cflags+= @@ -350,13 +358,11 @@ $(speedtest1.js): emcc.cflags+= $(speedtest1.js): $(speedtest1.c) $(sqlite3-wasm.c) $(MAKEFILE) $(sqlite3.c) @echo "Building $@ ..." $(emcc.bin) \ - $(emcc.speedtest1-flags) $(speedtest1.cflags) \ + $(speedtest1.eflags) $(speedtest1-common.eflags) $(speedtest1.cflags) \ $(SQLITE_OPT) \ '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' \ -o $@ $(speedtest1.c) $(sqlite3-wasm.c) -lm -ifneq (,$(wasm-strip)) - $(wasm-strip) $(speedtest1.wasm) -endif + $(maybe-wasm-strip) $(speedtest1.wasm) ls -la $@ $(speedtest1.wasm) speedtest1: $(speedtest1.js) @@ -388,5 +394,26 @@ push-fiddle: $(fiddle_files) # end fiddle remote push ######################################################################## +######################################################################## +# Convenience rules to rebuild with various -Ox levels. Much +# experimentation shows -O2 to be the clear winner in terms of speed. +# Note that build times with anything higher than -O0 are somewhat +# painful. +.PHONY: o0 o1 o2 o3 os oz +o0: + $(MAKE) clean; $(MAKE) -e emcc_opt=-O0 +o1: + $(MAKE) clean; $(MAKE) -e emcc_opt=-O1 +o2: + $(MAKE) clean; $(MAKE) -e emcc_opt=-O2 +o3: + $(MAKE) clean; $(MAKE) -e emcc_opt=-O3 +os: + $(MAKE) clean; $(MAKE) -e emcc_opt=-Os +oz: + $(MAKE) clean; $(MAKE) -e emcc_opt=-Oz + +######################################################################## +# Sub-makes... include kvvfs.make include wasmfs.make diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 6a8c229392..368986933f 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -1383,12 +1383,11 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ return this.reset(); }, /** - Functions like step() except that - it finalizes this statement immediately after stepping unless - the step cannot be performed because the statement is - locked. Throws on error, but any error other than the - statement-is-locked case will also trigger finalization of this - statement. + Functions like step() except that it finalizes this statement + immediately after stepping unless the step cannot be performed + because the statement is locked. Throws on error, but any error + other than the statement-is-locked case will also trigger + finalization of this statement. On success, it returns true if the step indicated that a row of data was available, else it returns false. diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 1c22e9ea21..3d6d4c92b5 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -720,7 +720,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( } try{ if(pdir && 0===capi.wasm.xCallWrapped( - 'sqlite3_wasm_init_opfs', 'i32', ['string'], pdir + 'sqlite3_wasm_init_wasmfs', 'i32', ['string'], pdir )){ /** OPFS does not support locking and will trigger errors if we try to lock. We don't _really_ want to @@ -739,7 +739,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( return __persistentDir = ""; } }catch(e){ - // sqlite3_wasm_init_opfs() is not available + // sqlite3_wasm_init_wasmfs() is not available return __persistentDir = ""; } }; diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index c072e8c9d4..eb8f58b402 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -510,7 +510,7 @@ int sqlite3_wasm_vfs_unlink(const char * zName){ return rc; } -#if defined(__EMSCRIPTEN__) && defined(SQLITE_WASM_OPFS) +#if defined(__EMSCRIPTEN__) && defined(SQLITE_WASM_WASMFS) #include #include @@ -532,11 +532,11 @@ int sqlite3_wasm_vfs_unlink(const char * zName){ ** ** Returns 0 on success, SQLITE_NOMEM if instantiation of the backend ** object fails, SQLITE_IOERR if mkdir() of the zMountPoint dir in -** the virtual FS fails. In builds compiled without SQLITE_WASM_OPFS +** the virtual FS fails. In builds compiled without SQLITE_WASM_WASMFS ** defined, SQLITE_NOTFOUND is returned without side effects. */ WASM_KEEP -int sqlite3_wasm_init_opfs(const char *zMountPoint){ +int sqlite3_wasm_init_wasmfs(const char *zMountPoint){ static backend_t pOpfs = 0; if( !zMountPoint || !*zMountPoint ) zMountPoint = "/persistent"; if( !pOpfs ){ @@ -562,10 +562,10 @@ int sqlite3_wasm_init_opfs(const char *zMountPoint){ } #else WASM_KEEP -int sqlite3_wasm_init_opfs(void){ +int sqlite3_wasm_init_wasmfs(void){ return SQLITE_NOTFOUND; } -#endif /* __EMSCRIPTEN__ && SQLITE_WASM_OPFS */ +#endif /* __EMSCRIPTEN__ && SQLITE_WASM_WASMFS */ #undef WASM_KEEP diff --git a/ext/wasm/index.html b/ext/wasm/index.html index 4b21839a4a..383045ea2c 100644 --- a/ext/wasm/index.html +++ b/ext/wasm/index.html @@ -39,6 +39,7 @@
  • batch-runner: runs batches of SQL exported from speedtest1.
  • speedtest1: a main-thread WASM build of speedtest1.
  • speedtest1-worker: an interactive Worker-thread variant of speedtest1.
  • +
  • speedtest1-wasmfs: a variant of speedtest1 built solely for the wasmfs/opfs feature.
  • speedtest1-kvvfs: a variant of speedtest1 built solely for the kv-vfs feature.
  • kvvfs1: very basic demo of using the key-value vfs for storing a persistent db in JS localStorage or sessionStorage.
  • diff --git a/ext/wasm/kvvfs.make b/ext/wasm/kvvfs.make index 3ef9dbddf9..293bcee04f 100644 --- a/ext/wasm/kvvfs.make +++ b/ext/wasm/kvvfs.make @@ -88,9 +88,7 @@ $(kvvfs.js): $(kvvfs.wasm.c) $(sqlite3.c) $(kvvfs.extra.c) \ $(SQLITE_OPT) \ $(kvvfs.cflags) $(kvvfs.jsflags) $(kvvfs.wasm.c) $(kvvfs.extra.c) chmod -x $(kvvfs.wasm) -ifneq (,$(wasm-strip)) - $(wasm-strip) $(kvvfs.wasm) -endif + $(maybe-wasm-strip) $(kvvfs.wasm) @ls -la $@ $(kvvfs.wasm) kvvfs: $(kvvfs.js) @@ -103,14 +101,12 @@ speedtest1-kvvfs.wasm := speedtest1-kvvfs.wasm CLEAN_FILES += $(speedtest1-kvvfs.js) $(speedtest1-kvvfs.wasm) $(speedtest1-kvvfs.js): $(speedtest1.c) $(sqlite3-wasm.c) $(sqlite3.c) $(MAKEFILE.kvvfs) $(emcc.bin) \ - $(emcc.speedtest1-flags) $(speedtest1.cflags) \ + $(speedtest1.eflags) $(speedtest1-common.eflags) $(speedtest1.cflags) \ $(SQLITE_OPT) \ -sEXIT_RUNTIME=1 \ $(kvvfs.cflags) \ -o $@ $(speedtest1.c) $(sqlite3-wasm.c) -lm -ifneq (,$(wasm-strip)) - $(wasm-strip) $(speedtest1-kvvfs.wasm) -endif + $(maybe-wasm-strip) $(speedtest1-kvvfs.wasm) ls -la $@ $(speedtest1-kvvfs.wasm) speedtest1: $(speedtest1-kvvfs.js) diff --git a/ext/wasm/scratchpad-opfs-worker.js b/ext/wasm/scratchpad-opfs-worker.js index debd0245e0..e27164775c 100644 --- a/ext/wasm/scratchpad-opfs-worker.js +++ b/ext/wasm/scratchpad-opfs-worker.js @@ -16,11 +16,26 @@ 'use strict'; (function(){ const toss = function(...args){throw new Error(args.join(' '))}; - const log = console.log.bind(console), - warn = console.warn.bind(console), - error = console.error.bind(console); + const eOutput = document.querySelector('#test-output'); + const logHtml = function(cssClass,...args){ + if(Array.isArray(args[0])) args = args[0]; + const ln = document.createElement('div'); + if(cssClass) ln.classList.add(cssClass); + ln.append(document.createTextNode(args.join(' '))); + eOutput.append(ln); + }; + const log = function(...args){ + logHtml('',...args); + }; + const error = function(...args){ + logHtml('error',...args); + }; + const warn = function(...args){ + logHtml('warning',...args); + }; + const W = new Worker("scratchpad-opfs-worker2.js"); - self.onmessage = function(ev){ + W.onmessage = function(ev){ ev = ev.data; const d = ev.data; switch(ev.type){ diff --git a/ext/wasm/scratchpad-opfs-worker2.js b/ext/wasm/scratchpad-opfs-worker2.js index 04b6bfc274..64e5266db2 100644 --- a/ext/wasm/scratchpad-opfs-worker2.js +++ b/ext/wasm/scratchpad-opfs-worker2.js @@ -16,25 +16,404 @@ is, quite frankly, broken). */ 'use strict'; -(function(){ - const toss = function(...args){throw new Error(args.join(' '))}; - importScripts('sqlite3.js'); + +const toss = function(...args){throw new Error(args.join(' '))}; +/** + 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(...args){ + wMsg('stdout',args); + console.log(...args); +}; +const stderr = function(...args){ + wMsg('stderr',args); + console.error(...args); +}; + +const log = console.log.bind(console); +const warn = console.warn.bind(console); +const error = console.error.bind(console); + + +const initOpfsBits = async function(sqlite3){ + if(!self.importScripts || !self.FileSystemFileHandle){ + //|| !self.FileSystemFileHandle.prototype.createSyncAccessHandle){ + // ^^^ sync API is not required with WASMFS/OPFS backend. + warn("OPFS is not available in this environment."); + return; + }else if(!sqlite3.capi.wasm.bigIntEnabled){ + error("OPFS requires BigInt support but sqlite3.capi.wasm.bigIntEnabled is false."); + return; + } + //warn('self.FileSystemFileHandle =',self.FileSystemFileHandle); + //warn('self.FileSystemFileHandle.prototype =',self.FileSystemFileHandle.prototype); + const capi = sqlite3.capi, + wasm = capi.wasm; + const sqlite3_vfs = capi.sqlite3_vfs + || toss("Missing sqlite3.capi.sqlite3_vfs object."); + const sqlite3_file = capi.sqlite3_file + || toss("Missing sqlite3.capi.sqlite3_file object."); + const sqlite3_io_methods = capi.sqlite3_io_methods + || toss("Missing sqlite3.capi.sqlite3_io_methods object."); + const StructBinder = sqlite3.StructBinder || toss("Missing sqlite3.StructBinder."); + const debug = console.debug.bind(console), + log = console.log.bind(console); + warn("UNDER CONSTRUCTION: setting up OPFS VFS..."); + + const pDVfs = capi.sqlite3_vfs_find(null)/*pointer to default VFS*/; + const dVfs = pDVfs + ? new sqlite3_vfs(pDVfs) + : null /* dVfs will be null when sqlite3 is built with + SQLITE_OS_OTHER. Though we cannot currently handle + that case, the hope is to eventually be able to. */; + const oVfs = new sqlite3_vfs(); + const oIom = new sqlite3_io_methods(); + oVfs.$iVersion = 2/*yes, two*/; + oVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; + oVfs.$mxPathname = 1024/*sure, why not?*/; + oVfs.$zName = wasm.allocCString("opfs"); + oVfs.ondispose = [ + '$zName', oVfs.$zName, + 'cleanup dVfs', ()=>(dVfs ? dVfs.dispose() : null) + ]; + if(dVfs){ + oVfs.$xSleep = dVfs.$xSleep; + oVfs.$xRandomness = dVfs.$xRandomness; + } + // All C-side memory of oVfs is zeroed out, but just to be explicit: + oVfs.$xDlOpen = oVfs.$xDlError = oVfs.$xDlSym = oVfs.$xDlClose = null; /** - Posts a message in the form {type,data} unless passed more than 2 - args, in which case it posts {type, data:[arg1...argN]}. + Pedantic sidebar about oVfs.ondispose: the entries in that array + are items to clean up when oVfs.dispose() is called, but in this + environment it will never be called. The VFS instance simply + hangs around until the WASM module instance is cleaned up. We + "could" _hypothetically_ clean it up by "importing" an + sqlite3_os_end() impl into the wasm build, but the shutdown order + of the wasm engine and the JS one are undefined so there is no + guaranty that the oVfs instance would be available in one + environment or the other when sqlite3_os_end() is called (_if_ it + gets called at all in a wasm build, which is undefined). */ - const wMsg = function(type,data){ - postMessage({ - type, - data: arguments.length<3 - ? data - : Array.prototype.slice.call(arguments,1) - }); + + /** + Installs a StructBinder-bound function pointer member of the + given name and function in the given StructType target object. + It creates a WASM proxy for the given function and arranges for + that proxy to be cleaned up when tgt.dispose() is called. Throws + on the slightest hint of error (e.g. tgt is-not-a StructType, + name does not map to a struct-bound member, etc.). + + Returns a proxy for this function which is bound to tgt and takes + 2 args (name,func). That function returns the same thing, + permitting calls to be chained. + + If called with only 1 arg, it has no side effects but returns a + func with the same signature as described above. + */ + const installMethod = function callee(tgt, name, func){ + if(!(tgt instanceof StructBinder.StructType)){ + toss("Usage error: target object is-not-a StructType."); + } + if(1===arguments.length){ + return (n,f)=>callee(tgt,n,f); + } + if(!callee.argcProxy){ + callee.argcProxy = function(func,sig){ + return function(...args){ + if(func.length!==arguments.length){ + toss("Argument mismatch. Native signature is:",sig); + } + return func.apply(this, args); + } + }; + callee.removeFuncList = function(){ + if(this.ondispose.__removeFuncList){ + this.ondispose.__removeFuncList.forEach( + (v,ndx)=>{ + if('number'===typeof v){ + try{wasm.uninstallFunction(v)} + catch(e){/*ignore*/} + } + /* else it's a descriptive label for the next number in + the list. */ + } + ); + delete this.ondispose.__removeFuncList; + } + }; + }/*static init*/ + const sigN = tgt.memberSignature(name); + if(sigN.length<2){ + toss("Member",name," is not a function pointer. Signature =",sigN); + } + const memKey = tgt.memberKey(name); + //log("installMethod",tgt, name, sigN); + const fProxy = 1 + // We can remove this proxy middle-man once the VFS is working + ? callee.argcProxy(func, sigN) + : func; + const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true)); + tgt[memKey] = pFunc; + if(!tgt.ondispose) tgt.ondispose = []; + if(!tgt.ondispose.__removeFuncList){ + tgt.ondispose.push('ondispose.__removeFuncList handler', + callee.removeFuncList); + tgt.ondispose.__removeFuncList = []; + } + tgt.ondispose.__removeFuncList.push(memKey, pFunc); + return (n,f)=>callee(tgt, n, f); + }/*installMethod*/; + + /** + Map of sqlite3_file pointers to OPFS handles. + */ + const __opfsHandles = Object.create(null); + + /** + Generates a random ASCII string len characters long, intended for + use as a temporary file name. + */ + const randomFilename = function f(len=16){ + if(!f._chars){ + f._chars = "abcdefghijklmnopqrstuvwxyz"+ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ + "012346789"; + f._n = f._chars.length; + } + const a = []; + let i = 0; + for( ; i < len; ++i){ + const ndx = Math.random() * (f._n * 64) % f._n | 0; + a[i] = f._chars[ndx]; + } + return a.join(''); }; - const stdout = console.log.bind(console); - const stderr = console.error.bind(console);//function(...args){wMsg('stderr', args);}; + const rootDir = await navigator.storage.getDirectory(); + log("rootDir =",rootDir); + + //////////////////////////////////////////////////////////////////////// + // Set up OPFS VFS methods... + let inst = installMethod(oVfs); + inst('xOpen', function(pVfs, zName, pFile, flags, pOutFlags){ + const f = new sqlite3_file(pFile); + f.$pMethods = oIom.pointer; + __opfsHandles[pFile] = f; + f.opfsHandle = null /* TODO */; + if(flags & capi.SQLITE_OPEN_DELETEONCLOSE){ + f.deleteOnClose = true; + } + f.filename = zName ? wasm.cstringToJs(zName) : 'sqlite3-xOpen-'+randomFilename(); + error("OPFS sqlite3_vfs::xOpen is not yet full implemented."); + return capi.SQLITE_IOERR; + }) + ('xFullPathname', function(pVfs,zName,nOut,pOut){ + /* Until/unless we have some notion of "current dir" + in OPFS, simply copy zName to pOut... */ + const i = wasm.cstrncpy(pOut, zName, nOut); + return i remove() + return capi.SQLITE_IOERR; + }) + ('xGetLastError', function(pVfs,nOut,pOut){ + debug("OPFS sqlite3_vfs::xGetLastError() has nothing sensible to return."); + return 0; + }) + ('xCurrentTime', function(pVfs,pOut){ + /* If it turns out that we need to adjust for timezone, see: + https://stackoverflow.com/a/11760121/1458521 */ + wasm.setMemValue(pOut, 2440587.5 + (new Date().getTime()/86400000), + 'double'); + return 0; + }) + ('xCurrentTimeInt64',function(pVfs,pOut){ + // TODO: confirm that this calculation is correct + wasm.setMemValue(pOut, (2440587.5 * 86400000) + new Date().getTime(), + 'i64'); + return 0; + }); + if(!oVfs.$xSleep){ + inst('xSleep', function(pVfs,ms){ + error("sqlite3_vfs::xSleep(",ms,") cannot be implemented from "+ + "JS and we have no default VFS to copy the impl from."); + return 0; + }); + } + if(!oVfs.$xRandomness){ + inst('xRandomness', function(pVfs, nOut, pOut){ + const heap = wasm.heap8u(); + let i = 0; + for(; i < nOut; ++i) heap[pOut + i] = (Math.random()*255000) & 0xFF; + return i; + }); + } + + //////////////////////////////////////////////////////////////////////// + // Set up OPFS sqlite3_io_methods... + inst = installMethod(oIom); + inst('xClose', async function(pFile){ + warn("xClose(",arguments,") uses await"); + const f = __opfsHandles[pFile]; + delete __opfsHandles[pFile]; + if(f.opfsHandle){ + await f.opfsHandle.close(); + if(f.deleteOnClose){ + // TODO + } + } + f.dispose(); + return 0; + }) + ('xRead', /*i(ppij)*/function(pFile,pDest,n,offset){ + /* int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) */ + try { + const f = __opfsHandles[pFile]; + const heap = wasm.heap8u(); + const b = new Uint8Array(heap.buffer, pDest, n); + const nRead = f.opfsHandle.read(b, {at: offset}); + if(nRead SQLITE_DEFAULT_SECTOR_SIZE */; + //}) + + const rc = capi.sqlite3_vfs_register(oVfs.pointer, 0); + if(rc){ + oVfs.dispose(); + toss("sqlite3_vfs_register(OPFS) failed with rc",rc); + } + capi.sqlite3_vfs_register.addReference(oVfs, oIom); + warn("End of (very incomplete) OPFS setup.", oVfs); + //oVfs.dispose()/*only because we can't yet do anything with it*/; + +}/*initOpfsBits()*/; + +(async function(){ + importScripts('sqlite3.js'); const test1 = function(db){ db.exec("create table if not exists t(a);") @@ -47,7 +426,7 @@ }); }; - const runTests = function(Module){ + const runTests = async function(Module){ //stdout("Module",Module); self._MODULE = Module /* this is only to facilitate testing from the console */; const sqlite3 = Module.sqlite3, @@ -55,11 +434,39 @@ oo = sqlite3.oo1, wasm = capi.wasm; stdout("Loaded sqlite3:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); - const persistentDir = capi.sqlite3_web_persistent_dir(); - if(persistentDir){ - stderr("Persistent storage dir:",persistentDir); + + if(1){ + let errCount = 0; + [ + 'FileSystemHandle', 'FileSystemFileHandle', 'FileSystemDirectoryHandle', + 'FileSystemHandle', 'FileSystemFileHandle', 'FileSystemDirectoryHandle' + ].forEach(function(n){ + const f = self[n]; + if(f){ + warn(n,f); + warn(n+'.prototype',f.prototype); + }else{ + stderr("MISSING",n); + ++errCount; + } + }); + if(errCount) return; + } + await initOpfsBits(sqlite3); + + if(1) return; + + let persistentDir; + if(1){ + persistentDir = ''; }else{ - stderr("No persistent storage available."); + persistentDir = capi.sqlite3_web_persistent_dir(); + if(persistentDir){ + stderr("Persistent storage dir:",persistentDir); + }else{ + stderr("No persistent storage available."); + return; + } } const startTime = performance.now(); let db; diff --git a/ext/wasm/speedtest1-wasmfs.html b/ext/wasm/speedtest1-wasmfs.html new file mode 100644 index 0000000000..151ced89a3 --- /dev/null +++ b/ext/wasm/speedtest1-wasmfs.html @@ -0,0 +1,159 @@ + + + + + + + + + speedtest1-wasmfs.wasm + + +
    speedtest1-wasmfs.wasm
    + + +
    +
    +
    Initializing app...
    +
    + 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. +
    +
    +
    Downloading...
    +
    + +
    +
    This page starts running the main exe when it loads, which will + block the UI until it finishes! Adding UI controls to manually configure and start it + are TODO.
    + +
    Achtung: running it with the dev tools open may + drastically slow it down. For faster results, keep the dev + tools closed when running it! +
    +
    Output is delayed/buffered because we cannot update the UI while the + speedtest is running. Output will appear below when ready... +
    + + + + + + diff --git a/ext/wasm/speedtest1-worker.html b/ext/wasm/speedtest1-worker.html index ba74d9f7c6..0e0ca4c464 100644 --- a/ext/wasm/speedtest1-worker.html +++ b/ext/wasm/speedtest1-worker.html @@ -34,9 +34,14 @@
    @@ -167,6 +172,7 @@ const eFlags = E('#select-flags'); const eSelectedFlags = E('#toolbar-selected-flags'); const eLinkMainThread = E('#link-main-thread'); + const eLinkWasmfs = E('#link-wasmfs'); const getSelectedFlags = ()=>Array.prototype.map.call(eFlags.selectedOptions, (v)=>v.value); const updateSelectedFlags = function(){ eSelectedFlags.innerText = ''; @@ -178,8 +184,10 @@ }); const rxStripDash = /^(-+)?/; const comma = flags.map((v)=>v.replace(rxStripDash,'')).join(','); - eLinkMainThread.setAttribute('target', 'main-thread-'+comma); + eLinkMainThread.setAttribute('target', 'speedtest1-main-'+comma); eLinkMainThread.href = 'speedtest1.html?flags='+comma; + eLinkWasmfs.setAttribute('target', 'speedtest1-wasmfs-'+comma); + eLinkWasmfs.href = 'speedtest1-wasmfs.html?flags='+comma; }; eFlags.addEventListener('change', updateSelectedFlags ); { diff --git a/ext/wasm/speedtest1-worker.js b/ext/wasm/speedtest1-worker.js index 8512bdbbf7..b954f9939a 100644 --- a/ext/wasm/speedtest1-worker.js +++ b/ext/wasm/speedtest1-worker.js @@ -16,14 +16,14 @@ } try{ if(0===wasmUtil.xCallWrapped( - 'sqlite3_wasm_init_opfs', 'i32', ['string'], pdir + 'sqlite3_wasm_init_wasmfs', 'i32', ['string'], pdir )){ return f._ = pdir; }else{ return f._ = ""; } }catch(e){ - // sqlite3_wasm_init_opfs() is not available + // sqlite3_wasm_init_wasmfs() is not available return f._ = ""; } }; @@ -91,9 +91,8 @@ self.WhWasmUtilInstaller(App.wasm); App.unlink = App.wasm.xWrap("sqlite3_wasm_vfs_unlink", "int", ["string"]); App.pDir = opfsDir(App.wasm); - if(App.pDir){ - log("Persistent storage:",pDir); - } + if(App.pDir) log("Persistent storage:",pDir); + else log("Using transient storage."); mPost('ready',true); }); })(); diff --git a/ext/wasm/speedtest1.html b/ext/wasm/speedtest1.html index bc8b4ec9f1..172266aa68 100644 --- a/ext/wasm/speedtest1.html +++ b/ext/wasm/speedtest1.html @@ -55,14 +55,14 @@ } try{ if(0===wasmUtil.xCallWrapped( - 'sqlite3_wasm_init_opfs', 'i32', ['string'], pdir + 'sqlite3_wasm_init_wasmfs', 'i32', ['string'], pdir )){ return f._ = pdir; }else{ return f._ = ""; } }catch(e){ - // sqlite3_wasm_init_opfs() is not available + // sqlite3_wasm_init_wasmfs() is not available return f._ = ""; } }; @@ -122,9 +122,9 @@ "--nomemstat" ); //"--memdb", // note that memdb trumps the filename arg - argv.push("--big-transactions"/*important for tests 410 and 510!*/, - dbFile); } + argv.push("--big-transactions"/*important for tests 410 and 510!*/, + dbFile); console.log("argv =",argv); // These log messages are not emitted to the UI until after main() returns. Fixing that // requires moving the main() call and related cleanup into a timeout handler. diff --git a/ext/wasm/wasmfs.make b/ext/wasm/wasmfs.make index f69da43b9e..cc10e58877 100644 --- a/ext/wasm/wasmfs.make +++ b/ext/wasm/wasmfs.make @@ -7,89 +7,114 @@ ######################################################################## MAKEFILE.wasmfs := $(lastword $(MAKEFILE_LIST)) -wasmfs.js := sqlite3-wasmfs.js -wasmfs.wasm := sqlite3-wasmfs.wasm -wasmfs.wasm.c := $(dir.api)/sqlite3-wasm.c +sqlite3-wasmfs.js := sqlite3-wasmfs.js +sqlite3-wasmfs.wasm := sqlite3-wasmfs.wasm +sqlite3-wasmfs.wasm.c := $(dir.api)/sqlite3-wasm.c -CLEAN_FILES += $(wasmfs.js) $(wasmfs.wasm) +CLEAN_FILES += $(sqlite3-wasmfs.js) $(sqlite3-wasmfs.wasm) \ + $(subst .js,.worker.js,$(sqlite3-wasmfs.js)) ######################################################################## # emcc flags for .c/.o/.wasm. -wasmfs.flags = -#wasmfs.flags += -v # _very_ loud but also informative about what it's doing +sqlite3-wasmfs.flags = +#sqlite3-wasmfs.flags += -v # _very_ loud but also informative about what it's doing ######################################################################## # emcc flags for .c/.o. -wasmfs.cflags := -wasmfs.cflags += -std=c99 -fPIC -g -wasmfs.cflags += -pthread -wasmfs.cflags += -I. -I$(dir.top) -wasmfs.cflags += $(SQLITE_OPT) -DSQLITE_WASM_OPFS -wasmfs.cflags += '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' +sqlite3-wasmfs.cflags := +sqlite3-wasmfs.cflags += -std=c99 -fPIC -g +sqlite3-wasmfs.cflags += -pthread +sqlite3-wasmfs.cflags += -I. -I.. -I$(dir.top) +sqlite3-wasmfs.cflags += $(SQLITE_OPT) -DSQLITE_WASM_WASMFS +sqlite3-wasmfs.cflags += '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' -wasmfs.extra.c := +sqlite3-wasmfs.extra.c := ifeq (1,1) - # To get testing1.js to run with $(wasmfs.js) we need... - wasmfs.extra.c += $(jaccwabyt_test.c) + # To get testing1.js to run with $(sqlite3-wasmfs.js) we need... + sqlite3-wasmfs.extra.c += $(jaccwabyt_test.c) endif ######################################################################## # emcc flags specific to building the final .js/.wasm file... -wasmfs.jsflags := -fPIC -wasmfs.jsflags += --no-entry -wasmfs.jsflags += --minify 0 -wasmfs.jsflags += -sENVIRONMENT=web,worker -wasmfs.jsflags += -sMODULARIZE -wasmfs.jsflags += -sSTRICT_JS -wasmfs.jsflags += -sDYNAMIC_EXECUTION=0 -wasmfs.jsflags += -sNO_POLYFILL -ifeq (,$(wasmfs.extra.c)) - wasmfs.jsflags += -sEXPORTED_FUNCTIONS=@$(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api +sqlite3-wasmfs.jsflags := -fPIC +sqlite3-wasmfs.jsflags += --no-entry +sqlite3-wasmfs.jsflags += --minify 0 +sqlite3-wasmfs.jsflags += -sMODULARIZE +sqlite3-wasmfs.jsflags += -sSTRICT_JS +sqlite3-wasmfs.jsflags += -sDYNAMIC_EXECUTION=0 +sqlite3-wasmfs.jsflags += -sNO_POLYFILL +ifeq (,$(sqlite3-wasmfs.extra.c)) + sqlite3-wasmfs.jsflags += -sEXPORTED_FUNCTIONS=@$(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api else # need more exports for jaccwabyt test code... - wasmfs.jsflags += -sEXPORTED_FUNCTIONS=@$(dir.wasm)/EXPORTED_FUNCTIONS.api + sqlite3-wasmfs.jsflags += -sEXPORTED_FUNCTIONS=@$(dir.wasm)/EXPORTED_FUNCTIONS.api endif -wasmfs.jsflags += -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory,allocateUTF8OnStack +sqlite3-wasmfs.jsflags += -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory,allocateUTF8OnStack # wasmMemory ==> for -sIMPORTED_MEMORY # allocateUTF8OnStack ==> wasmfs internals -wasmfs.jsflags += -sUSE_CLOSURE_COMPILER=0 -wasmfs.jsflags += -sIMPORTED_MEMORY -#wasmfs.jsflags += -sINITIAL_MEMORY=13107200 -#wasmfs.jsflags += -sTOTAL_STACK=4194304 -wasmfs.jsflags += -sEXPORT_NAME=sqlite3InitModule -wasmfs.jsflags += -sGLOBAL_BASE=4096 # HYPOTHETICALLY keep func table indexes from overlapping w/ heap addr. -wasmfs.jsflags += --post-js=$(post-js.js) -#wasmfs.jsflags += -sFILESYSTEM=0 # only for experimentation. sqlite3 needs the FS API +sqlite3-wasmfs.jsflags += -sUSE_CLOSURE_COMPILER=0 +sqlite3-wasmfs.jsflags += -sIMPORTED_MEMORY +#sqlite3-wasmfs.jsflags += -sINITIAL_MEMORY=13107200 +#sqlite3-wasmfs.jsflags += -sTOTAL_STACK=4194304 +sqlite3-wasmfs.jsflags += -sEXPORT_NAME=sqlite3InitModule +sqlite3-wasmfs.jsflags += -sGLOBAL_BASE=4096 # HYPOTHETICALLY keep func table indexes from overlapping w/ heap addr. +sqlite3-wasmfs.jsflags += --post-js=$(post-js.js) +#sqlite3-wasmfs.jsflags += -sFILESYSTEM=0 # only for experimentation. sqlite3 needs the FS API # Perhaps the wasmfs build doesn't? -#wasmfs.jsflags += -sABORTING_MALLOC -wasmfs.jsflags += -sALLOW_TABLE_GROWTH -wasmfs.jsflags += -Wno-limited-postlink-optimizations +#sqlite3-wasmfs.jsflags += -sABORTING_MALLOC +sqlite3-wasmfs.jsflags += -sALLOW_TABLE_GROWTH +sqlite3-wasmfs.jsflags += -Wno-limited-postlink-optimizations # ^^^^^ it likes to warn when we have "limited optimizations" via the -g3 flag. -wasmfs.jsflags += -sERROR_ON_UNDEFINED_SYMBOLS=0 -wasmfs.jsflags += -sLLD_REPORT_UNDEFINED -#wasmfs.jsflags += --import-undefined -wasmfs.jsflags += -sMEMORY64=0 -wasmfs.jsflags += -pthread -sWASMFS -sPTHREAD_POOL_SIZE=2 -wasmfs.jsflags += -sINITIAL_MEMORY=128450560 -#wasmfs.jsflags += -sALLOW_MEMORY_GROWTH +sqlite3-wasmfs.jsflags += -sERROR_ON_UNDEFINED_SYMBOLS=0 +sqlite3-wasmfs.jsflags += -sLLD_REPORT_UNDEFINED +#sqlite3-wasmfs.jsflags += --import-undefined +sqlite3-wasmfs.jsflags += -sMEMORY64=0 +sqlite3-wasmfs.jsflags += -sINITIAL_MEMORY=128450560 +sqlite3-wasmfs.fsflags := -pthread -sWASMFS -sPTHREAD_POOL_SIZE=2 -sENVIRONMENT=web,worker +sqlite3-wasmfs.jsflags += $(sqlite3-wasmfs.fsflags) +#sqlite3-wasmfs.jsflags += -sALLOW_MEMORY_GROWTH #^^^ using ALLOW_MEMORY_GROWTH produces a warning from emcc: # USE_PTHREADS + ALLOW_MEMORY_GROWTH may run non-wasm code slowly, # see https://github.com/WebAssembly/design/issues/1271 [-Wpthreads-mem-growth] ifneq (0,$(enable_bigint)) -wasmfs.jsflags += -sWASM_BIGINT +sqlite3-wasmfs.jsflags += -sWASM_BIGINT endif -$(wasmfs.js): $(wasmfs.wasm.c) $(sqlite3.c) $(wasmfs.extra.c) \ +$(sqlite3-wasmfs.js): $(sqlite3-wasmfs.wasm.c) $(sqlite3.c) $(sqlite3-wasmfs.extra.c) \ EXPORTED_FUNCTIONS.api $(MAKEFILE) $(MAKEFILE.wasmfs) \ $(post-js.js) @echo "Building $@ ..." $(emcc.bin) -o $@ $(emcc_opt) $(emcc.flags) \ - $(wasmfs.cflags) $(wasmfs.jsflags) $(wasmfs.wasm.c) $(wasmfs.extra.c) - chmod -x $(wasmfs.wasm) -ifneq (,$(wasm-strip)) - $(wasm-strip) $(wasmfs.wasm) -endif - @ls -la $@ $(wasmfs.wasm) + $(sqlite3-wasmfs.cflags) $(sqlite3-wasmfs.jsflags) $(sqlite3-wasmfs.wasm.c) $(sqlite3-wasmfs.extra.c) + chmod -x $(sqlite3-wasmfs.wasm) + $(maybe-wasm-strip) $(sqlite3-wasmfs.wasm) + @ls -la $@ $(sqlite3-wasmfs.wasm) -wasmfs: $(wasmfs.js) +wasmfs: $(sqlite3-wasmfs.js) all: wasmfs + +######################################################################## +# speedtest1 for wasmfs. Re. sqlite3-wasm.o vs sqlite3-wasm.c: +# building against the latter (predictably) results in a slightly +# faster binary. +speedtest1-wasmfs.js := speedtest1-wasmfs.js +speedtest1-wasmfs.wasm := $(subst .js,.wasm,$(speedtest1-wasmfs.js)) +speedtest1-wasmfs.eflags := -sENVIRONMENT=web,worker $(sqlite3-wasmfs.fsflags) +speedtest1-wasmfs.eflags += $(SQLITE_OPT) -DSQLITE_WASM_WASMFS +$(speedtest1-wasmfs.js): $(MAKEFILE) $(MAKEFILE.wasmfs) +#$(speedtest1-wasmfs.js): $(sqlite3-wasmfs.js) +$(speedtest1-wasmfs.js): $(speedtest1.c) $(sqlite3-wasm.c) + @echo "Building $@ ..." + $(emcc.bin) \ + $(speedtest1-wasmfs.eflags) $(speedtest1-common.eflags) \ + $(speedtest1.cflags) \ + $(sqlite3-wasmfs.cflags) \ + -o $@ $(speedtest1.c) $(sqlite3-wasm.c) -lm + $(maybe-wasm-strip) $(speedtest1-wasmfs.wasm) + ls -la $@ $(speedtest1-wasmfs.wasm) + +speedtest1: $(speedtest1-wasmfs.js) +CLEAN_FILES += $(speedtest1-wasmfs.js) $(speedtest1-wasmfs.wasm) \ + $(subst .js,.worker.js,$(speedtest1-wasmfs.js)) +# end speedtest1.js +######################################################################## diff --git a/manifest b/manifest index 543bbdbf9a..68aabcc46a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\ssome\sstale\scomments.\sGet\sscratchpad-opfs-worker2.js\srunning\sagain\sin\sprep\sfor\sreuse\sin\sexperimenting\swith\sthe\sOPFS\sall-synchronous\sAPI. -D 2022-09-15T03:16:49.402 +C More\swork\son\sthe\ssynchronous\sOPFS\sexperimentation.\sNumerous\swasm/js\sbuild\stweaks.\sAdd\sspeeedtest-wasmfs.html,\sthe\swasmfs/opfs\scounterpart\sof\sspeedtest1.html. +D 2022-09-15T06:42:41.192 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -472,9 +472,9 @@ F ext/session/test_session.c f433f68a8a8c64b0f5bc74dc725078f12483301ad4ae8375205 F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb -F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 +F ext/wasm/EXPORTED_FUNCTIONS.fiddle 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile f06963ecf1a8275755a568f84ad66e1c6a093e190cffd84f1414d8e75d3c5277 +F ext/wasm/GNUmakefile 104ad527fd7cf1b33dd8ac544c4e26c529c305e591d85a32999258750998b0b8 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 150a793a47205b8009ac934f3b6d6ebf67b965c072339aaa25ce808a19e116cc F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -483,12 +483,12 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 -F ext/wasm/api/sqlite3-api-oo1.js 8a8e464c2c786f4faa3fd5b48852818dd2938c3211d82b6176143990a976f8ec +F ext/wasm/api/sqlite3-api-oo1.js d7526517f7ad3f6bda16ad66d373bbb71b43168deef7af60eda5c9fe873d1387 F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 -F ext/wasm/api/sqlite3-api-prologue.js c496adc0cf2ae427ee900aad01c09db97850bd8b6737f2849cab207c8415b839 +F ext/wasm/api/sqlite3-api-prologue.js 7aff2d9b110d5a6e70dbf5fe44189d983d7886a465ad156176f4ad290447c97b F ext/wasm/api/sqlite3-api-worker1.js d33062afa045fd4be01ba4abc266801807472558b862b30056211b00c9c347b4 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 -F ext/wasm/api/sqlite3-wasm.c 7d1760f3a864a9ae16cfb71543829d946a54384bd07af99a3adf9ef32913705f +F ext/wasm/api/sqlite3-wasm.c 4130e2df9587f4e4c3afc04c3549d682c8a5c0cfe5b22819a0a86edb7f01b9bd F ext/wasm/batch-runner.html 2857a6db7292ac83d1581af865d643fd34235db2df830d10b43b01388c599e04 F ext/wasm/batch-runner.js fb6a338aeef509f181f499700a8595bc578bbc54ef832e0cda7e7e7c10b90a18 F ext/wasm/common/SqliteTestUtil.js 529161a624265ba84271a52db58da022649832fa1c71309fb1e02cc037327a2b @@ -501,23 +501,24 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 -F ext/wasm/index.html 7e38070c8c64738eb3893be19e1291d66629fc424f1a70e12c4b459d51dbeffe +F ext/wasm/index.html e251800a64cf13e731ddec5a86ca4a299dcc4c1fafafd87658ccca3c538dd843 F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19 -F ext/wasm/kvvfs.make 99d61a438232d03f7a39fff05a5738d00d5af629e54b3f03caf87f041c194d51 +F ext/wasm/kvvfs.make 6f19ef9bdf0394e752b62717db968268206c5237a87541efb825d58945694ff0 F ext/wasm/kvvfs1.html 13bb24190bfb276a57b228499519badcc1bf39ed07e4b37bc2a425ce6418fed1 F ext/wasm/kvvfs1.js ec1c1d071bb055711f9151df05616111432cf3e6bf7ac7f8dcbcfb56c9d9ed48 F ext/wasm/scratchpad-opfs-worker.html 5fdda167571264300f388847d34f00b77dd48984a8dba2ee9c099c3ffa05db66 -F ext/wasm/scratchpad-opfs-worker.js 3ec2868c669713145c76eb5877c64a1b20741f741817b87c907a154b676283a9 -F ext/wasm/scratchpad-opfs-worker2.js 8cd4961e1c96fbaad9743a34cd17d0d27b9286b845e77242d444a4c78fe2a810 +F ext/wasm/scratchpad-opfs-worker.js cf6c4554d3b099c1a50013e50d19b3dc60e183511b4b4dbe7fabc2b9d3360567 +F ext/wasm/scratchpad-opfs-worker2.js 2424d7d7b8801fc143f6540fbdd8a96f3f2e5b811f0f545714d06147ccce58bf F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06 F ext/wasm/scratchpad-wasmfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e F ext/wasm/speedtest1-kvvfs.html fe7d314343b275f8b241af83267ca56274ea995535c6abca4d6e568a98fa31e3 -F ext/wasm/speedtest1-worker.html 6b5fda04d0b69e8c2651689356cb0c28fd33aa1a82b03dcbc8b0d68fbd7ed57f -F ext/wasm/speedtest1-worker.js 356b9953add4449acf199793db9b76b11ee016021918d8daffd19f08ec68d305 -F ext/wasm/speedtest1.html 8f61cbe68300acca25dd9fa74dce79b774786e2b4feeb9bcbc46e1cefbfa6262 +F ext/wasm/speedtest1-wasmfs.html 6a67a6812f03a2058eb5c6ad0c8dea4bf749d0160ed9d6b826dabe7b766c3cf7 +F ext/wasm/speedtest1-worker.html a1dc7611026adad43ddb138172996e13a96dd2d37dcdcb7cef39a130f6b1f57b +F ext/wasm/speedtest1-worker.js fb5d282c0b8aed18daf41c57f768cbf434f8137dbff707d53dcedcd7d4cb60ef +F ext/wasm/speedtest1.html fbb8e4d1639028443f3687a683be660beca6927920545cf6b1fdf503104591c0 F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 @@ -529,7 +530,7 @@ F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d766 F ext/wasm/testing1.js 7cd8ab255c238b030d928755ae8e91e7d90a12f2ae601b1b8f7827aaa4fb258e F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c -F ext/wasm/wasmfs.make 44c35734f531199dab7c49f82ed265344c86aa03d741fd7911ddb4de0c551ddd +F ext/wasm/wasmfs.make 21a5cf297954a689e0dc2a95299ae158f681cae5e90c10b99d986097815fd42d F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2025,8 +2026,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 08476f3c218d45846e7496bdae0b06e2122466111fdf2aa2aabb1805b15ef982 -R 520f42f1cc70efba1aa8d99f68fa03bf +P 4cbbd370186f84e440f496e7f11c641d7dc1723acc46e4a31483392e0eb046e9 +R c453132da1df672225f03816e5e29221 U stephan -Z a50915abdf47f7036b78a2ce4e5a53d8 +Z e87dced3d077599bf1560627688e6f4f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d3cb0d93c3..7d6cc0aac1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4cbbd370186f84e440f496e7f11c641d7dc1723acc46e4a31483392e0eb046e9 \ No newline at end of file +00ee49a3a2c148480f614e49a0ee5b35a255758c0a53693f0b464b31e7a4045b \ No newline at end of file From 0144a8482a02e05bc3140ffcf1aa0348a81aec90 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 16 Sep 2022 01:05:19 +0000 Subject: [PATCH 111/428] Add return value checks to kvstorageWrite(), necessary for recognizing storage-full errors in the JS binding. speedtest1-kvvfs.html runs successfully on localStorage with --size 1 to 6 and starts failing with storage shortage at --size 7. FossilOrigin-Name: 13839759f8f45e4eb0aa6a5052801f3964d5b2dc2e427e5e91ee6692176381eb --- ext/wasm/GNUmakefile | 21 ++++++++++++++++++- ext/wasm/kvvfs.make | 2 +- ext/wasm/speedtest1-kvvfs.html | 37 ++++++++++++++++++++++++++------- ext/wasm/speedtest1-worker.html | 6 ++++++ manifest | 20 +++++++++--------- manifest.uuid | 2 +- src/os_kv.c | 24 +++++++++++---------- 7 files changed, 80 insertions(+), 32 deletions(-) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 390472dea2..fe3c7a61f8 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -330,6 +330,7 @@ all: batch # speedtest1.eflags = emcc flags used by main build of speedtest1 speedtest1-common.eflags := -g $(emcc_opt) speedtest1.eflags := +speedtest1.eflags += -sENVIRONMENT=web speedtest1-common.eflags += -sINVOKE_RUN=0 #speedtest1-common.eflags += --no-entry speedtest1-common.eflags += -flto @@ -342,7 +343,24 @@ speedtest1-common.eflags += -sEXPORTED_FUNCTIONS=_main,_malloc,_free speedtest1-common.eflags += -sDYNAMIC_EXECUTION=0 speedtest1-common.eflags += --minify 0 speedtest1-common.eflags += -sEXPORT_NAME=sqlite3Speedtest1InitModule -speedtest1.eflags += -sENVIRONMENT=web +speedtest1.exit-runtime0 := -sEXIT_RUNTIME=0 +speedtest1.exit-runtime1 := -sEXIT_RUNTIME=1 +# Re -sEXIT_RUNTIME=1 vs 0: if it's 1 and speedtest1 crashes, we get +# this error from emscripten: +# +# > native function `free` called after runtime exit (use +# NO_EXIT_RUNTIME to keep it alive after main() exits)) +# +# If it's 0 and it crashes, we get: +# +# > stdio streams had content in them that was not flushed. you should +# set EXIT_RUNTIME to 1 (see the FAQ), or make sure to emit a newline +# when you printf etc. +# +# and pending output is not flushed because it didn't end with a +# newline (by design). The lesser of the two evils seems to be +# -sEXIT_RUNTIME=1 but we need EXIT_RUNTIME=0 for the worker-based app +# which runs speedtest1 multiple times. speedtest1.js := speedtest1.js speedtest1.wasm := $(subst .js,.wasm,$(speedtest1.js)) @@ -360,6 +378,7 @@ $(speedtest1.js): $(speedtest1.c) $(sqlite3-wasm.c) $(MAKEFILE) $(sqlite3.c) $(emcc.bin) \ $(speedtest1.eflags) $(speedtest1-common.eflags) $(speedtest1.cflags) \ $(SQLITE_OPT) \ + $(speedtest1.exit-runtime0) \ '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' \ -o $@ $(speedtest1.c) $(sqlite3-wasm.c) -lm $(maybe-wasm-strip) $(speedtest1.wasm) diff --git a/ext/wasm/kvvfs.make b/ext/wasm/kvvfs.make index 293bcee04f..d9a9539566 100644 --- a/ext/wasm/kvvfs.make +++ b/ext/wasm/kvvfs.make @@ -103,7 +103,7 @@ $(speedtest1-kvvfs.js): $(speedtest1.c) $(sqlite3-wasm.c) $(sqlite3.c) $(MAKEFIL $(emcc.bin) \ $(speedtest1.eflags) $(speedtest1-common.eflags) $(speedtest1.cflags) \ $(SQLITE_OPT) \ - -sEXIT_RUNTIME=1 \ + $(speedtest1.exit-runtime1) \ $(kvvfs.cflags) \ -o $@ $(speedtest1.c) $(sqlite3-wasm.c) -lm $(maybe-wasm-strip) $(speedtest1-kvvfs.wasm) diff --git a/ext/wasm/speedtest1-kvvfs.html b/ext/wasm/speedtest1-kvvfs.html index 778790722f..272425262b 100644 --- a/ext/wasm/speedtest1-kvvfs.html +++ b/ext/wasm/speedtest1-kvvfs.html @@ -6,10 +6,10 @@ - speedtest1.wasm + speedtest1-kvvfs.wasm -
    speedtest1.wasm
    +
    speedtest1-kvvfs.wasm
    @@ -63,12 +63,28 @@ console.error(...args); logList.push('ERROR: '+args.join(' ')); }; + + const guessStorageSize = function(which=''){ + let sz = 0; + const prefix = 'kvvfs-'+which; + [localStorage,sessionStorage].forEach((s)=>{ + let i; + for(i = 0; i < s.length; ++i){ + const k = s.key(i); + if(k.startsWith(prefix)){ + sz += k.length; + sz += s.getItem(k).length; + } + } + }); + return sz; + }; const clearStorage = function(){ sessionStorage.clear(); localStorage.clear(); }; const runTests = function(EmscriptenModule){ - console.log("Module inited.",EmscriptenModule); + console.log("Module inited.",EmscriptenModule); const wasm = { exports: EmscriptenModule.asm, @@ -83,7 +99,7 @@ const scope = wasm.scopedAllocPush(); const dbFile = 0 ? "session" : "local"; const urlArgs = self.SqliteTestUtil.processUrlArgs(); - const argv = ["speedtest1", "--size", "1"]; + const argv = ["speedtest1", "--size", "5"]; if(urlArgs.flags){ // transform flags=a,b,c to ["--a", "--b", "--c"] argv.push(...(urlArgs.flags.split(',').map((v)=>'--'+v))); @@ -93,10 +109,14 @@ "--nomutex", "--nosync", "--nomemstat" + //"--sqlonly" ); //argv.push("--memdb" /* note that memdb trumps the filename arg */); - argv.push("--big-transactions"/*important for tests 410 and 510!*/, - dbFile); + } + argv.push("--big-transactions"/*important for tests 410 and 510!*/, + dbFile); + if(argv.indexOf('--memdb')>=0){ + log2('error',"WARNING: --memdb flag trumps db filename."); } console.log("argv =",argv); // These log messages are not emitted to the UI until after main() returns. Fixing that @@ -119,8 +139,9 @@ wasm.scopedAllocPop(scope); logList.unshift("Done running native main(). Output:"); dumpLogList(); - log2('warning',"Clearing session/local storage."); - clearStorage(); + log2('',"Approximate",dbFile,"storage usage:",guessStorageSize(),"bytes"); + log2('warning',"Clearing",dbFile,"storage."); + clearStorage(dbFile); }); }/*runTests()*/; diff --git a/ext/wasm/speedtest1-worker.html b/ext/wasm/speedtest1-worker.html index 0e0ca4c464..3c9740286f 100644 --- a/ext/wasm/speedtest1-worker.html +++ b/ext/wasm/speedtest1-worker.html @@ -41,6 +41,9 @@ speedtest1-wasmfs.html + speedtest1-kvvfs.html +
    @@ -173,6 +176,7 @@ const eSelectedFlags = E('#toolbar-selected-flags'); const eLinkMainThread = E('#link-main-thread'); const eLinkWasmfs = E('#link-wasmfs'); + const eLinkKvvfs = E('#link-kvvfs'); const getSelectedFlags = ()=>Array.prototype.map.call(eFlags.selectedOptions, (v)=>v.value); const updateSelectedFlags = function(){ eSelectedFlags.innerText = ''; @@ -188,6 +192,8 @@ eLinkMainThread.href = 'speedtest1.html?flags='+comma; eLinkWasmfs.setAttribute('target', 'speedtest1-wasmfs-'+comma); eLinkWasmfs.href = 'speedtest1-wasmfs.html?flags='+comma; + eLinkKvvfs.setAttribute('target', 'speedtest1-kvvfs-'+comma); + eLinkKvvfs.href = 'speedtest1-kvvfs.html?flags='+comma; }; eFlags.addEventListener('change', updateSelectedFlags ); { diff --git a/manifest b/manifest index 68aabcc46a..b9e43a7389 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\swork\son\sthe\ssynchronous\sOPFS\sexperimentation.\sNumerous\swasm/js\sbuild\stweaks.\sAdd\sspeeedtest-wasmfs.html,\sthe\swasmfs/opfs\scounterpart\sof\sspeedtest1.html. -D 2022-09-15T06:42:41.192 +C Add\sreturn\svalue\schecks\sto\skvstorageWrite(),\snecessary\sfor\srecognizing\sstorage-full\serrors\sin\sthe\sJS\sbinding.\sspeedtest1-kvvfs.html\sruns\ssuccessfully\son\slocalStorage\swith\s--size\s1\sto\s6\sand\sstarts\sfailing\swith\sstorage\sshortage\sat\s--size\s7. +D 2022-09-16T01:05:19.939 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 104ad527fd7cf1b33dd8ac544c4e26c529c305e591d85a32999258750998b0b8 +F ext/wasm/GNUmakefile bec8b98f00c81561215bd8adff9870a30508fd6813bccf1b821ddf073446525e F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 150a793a47205b8009ac934f3b6d6ebf67b965c072339aaa25ce808a19e116cc F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -506,7 +506,7 @@ F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab26 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19 -F ext/wasm/kvvfs.make 6f19ef9bdf0394e752b62717db968268206c5237a87541efb825d58945694ff0 +F ext/wasm/kvvfs.make 4b2ba6d061f3a52da9f5812f86f4faa80fb4d9456a152f6b0585dccd667a4e22 F ext/wasm/kvvfs1.html 13bb24190bfb276a57b228499519badcc1bf39ed07e4b37bc2a425ce6418fed1 F ext/wasm/kvvfs1.js ec1c1d071bb055711f9151df05616111432cf3e6bf7ac7f8dcbcfb56c9d9ed48 F ext/wasm/scratchpad-opfs-worker.html 5fdda167571264300f388847d34f00b77dd48984a8dba2ee9c099c3ffa05db66 @@ -514,9 +514,9 @@ F ext/wasm/scratchpad-opfs-worker.js cf6c4554d3b099c1a50013e50d19b3dc60e183511b4 F ext/wasm/scratchpad-opfs-worker2.js 2424d7d7b8801fc143f6540fbdd8a96f3f2e5b811f0f545714d06147ccce58bf F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06 F ext/wasm/scratchpad-wasmfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e -F ext/wasm/speedtest1-kvvfs.html fe7d314343b275f8b241af83267ca56274ea995535c6abca4d6e568a98fa31e3 +F ext/wasm/speedtest1-kvvfs.html c9cb752f9c81cdcf9ccdc09146bd8c9c77970ed44ab10908ed15f1cce95c0f8d F ext/wasm/speedtest1-wasmfs.html 6a67a6812f03a2058eb5c6ad0c8dea4bf749d0160ed9d6b826dabe7b766c3cf7 -F ext/wasm/speedtest1-worker.html a1dc7611026adad43ddb138172996e13a96dd2d37dcdcb7cef39a130f6b1f57b +F ext/wasm/speedtest1-worker.html d8881ae802d15fb8adb94049265173e99f350e07e1d4e6f9e1cbd8969fe63a04 F ext/wasm/speedtest1-worker.js fb5d282c0b8aed18daf41c57f768cbf434f8137dbff707d53dcedcd7d4cb60ef F ext/wasm/speedtest1.html fbb8e4d1639028443f3687a683be660beca6927920545cf6b1fdf503104591c0 F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x @@ -596,7 +596,7 @@ F src/notify.c 89a97dc854c3aa62ad5f384ef50c5a4a11d70fcc69f86de3e991573421130ed6 F src/os.c 0eb831ba3575af5277e47f4edd14fdfc90025c67eb25ce5cda634518d308d4e9 F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 -F src/os_kv.c f3c5f46f62ed495c7d17c752f58f250c04b944c9cf34060518511e4e7537658c +F src/os_kv.c d4909b439043183f9b6aec65528a7433cee49d41cca95b9a3a4c8fb6bbedc0c2 F src/os_setup.h 0711dbc4678f3ac52d7fe736951b6384a0615387c4ba5135a4764e4e31f4b6a6 F src/os_unix.c 0fa91925f0b8831fc0156a9c04d39d86f85baf9eef66c98712395e1715cb75cc F src/os_win.c 8d129ae3e59e0fa900e20d0ad789e96f2e08177f0b00b53cdda65c40331e0902 @@ -2026,8 +2026,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 4cbbd370186f84e440f496e7f11c641d7dc1723acc46e4a31483392e0eb046e9 -R c453132da1df672225f03816e5e29221 +P 00ee49a3a2c148480f614e49a0ee5b35a255758c0a53693f0b464b31e7a4045b +R a6e9c244c29b55941f4ad5b4e78621c2 U stephan -Z e87dced3d077599bf1560627688e6f4f +Z daf2b8c0bbcd601af0a5bcd00e934942 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7d6cc0aac1..b3a08b5d95 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -00ee49a3a2c148480f614e49a0ee5b35a255758c0a53693f0b464b31e7a4045b \ No newline at end of file +13839759f8f45e4eb0aa6a5052801f3964d5b2dc2e427e5e91ee6692176381eb \ No newline at end of file diff --git a/src/os_kv.c b/src/os_kv.c index 4651400b15..17d4c254e1 100644 --- a/src/os_kv.c +++ b/src/os_kv.c @@ -351,7 +351,7 @@ int sqlite3__wasm_emjs_kvvfs(int whichOp){ switch( whichOp ){ case 0: break; case 1: - kvstorageWrite(zClass, zKey, "world"); + rc = kvstorageWrite(zClass, zKey, "world"); break; case 2: { char buffer[128] = {0}; @@ -634,10 +634,10 @@ static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){ kvstorageRead(pFile->zClass, "sz", zData, sizeof(zData)-1); return strtoll(zData, 0, 0); } -static void kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){ +static int kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){ char zData[50]; sqlite3_snprintf(sizeof(zData), zData, "%lld", sz); - kvstorageWrite(pFile->zClass, "sz", zData); + return kvstorageWrite(pFile->zClass, "sz", zData); } /****** sqlite3_io_methods methods ******************************************/ @@ -788,7 +788,9 @@ static int kvvfsWriteDb( pgno = 1 + iOfst/iAmt; sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); kvvfsEncode(zBuf, iAmt, aData); - kvstorageWrite(pFile->zClass, zKey, aData); + if( kvstorageWrite(pFile->zClass, zKey, aData) ){ + return SQLITE_IOERR; + } if( iOfst+iAmt > pFile->szDb ){ pFile->szDb = iOfst + iAmt; } @@ -825,8 +827,7 @@ static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){ pgno++; } pFile->szDb = size; - kvvfsWriteFileSize(pFile, size); - return SQLITE_OK; + return kvvfsWriteFileSize(pFile, size) ? SQLITE_IOERR : SQLITE_OK; } return SQLITE_IOERR; } @@ -854,17 +855,18 @@ static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){ }while( n>0 ); zOut[i++] = ' '; kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]); - kvstorageWrite(pFile->zClass, "jrnl", zOut); + i = kvstorageWrite(pFile->zClass, "jrnl", zOut); sqlite3_free(zOut); - return SQLITE_OK; + return i ? SQLITE_IOERR : SQLITE_OK; } static int kvvfsSyncDb(sqlite3_file *pProtoFile, int flags){ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + int rc = SQLITE_OK; SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass)); - if( pFile->szDb>0 ){ - kvvfsWriteFileSize(pFile, pFile->szDb); + if( pFile->szDb>0 && 0!=kvvfsWriteFileSize(pFile, pFile->szDb) ){ + rc = SQLITE_IOERR; } - return SQLITE_OK; + return rc; } /* From d22cfa8c80e9d95ed4068083f7dd5ffbb8f29dda Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 16 Sep 2022 01:08:06 +0000 Subject: [PATCH 112/428] Pull the src/os_kv.c part of [13839759f8f4] into the kv-vfs branch. FossilOrigin-Name: e334449912d5176e355d02024a07ed867741f71c9d10ce6744ca800414bf3eeb --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/os_kv.c | 26 ++++++++++++++------------ 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/manifest b/manifest index e8ffc8d53e..da3fab544e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\san\sextraneous\skey-copy\sop\sin\sthe\sEM_JS\simpl\sof\skvstorageDelete(). -D 2022-09-12T20:21:34.667 +C Pull\sthe\ssrc/os_kv.c\spart\sof\s[13839759f8f4]\sinto\sthe\skv-vfs\sbranch. +D 2022-09-16T01:08:06.279 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -574,7 +574,7 @@ F src/notify.c 89a97dc854c3aa62ad5f384ef50c5a4a11d70fcc69f86de3e991573421130ed6 F src/os.c 0eb831ba3575af5277e47f4edd14fdfc90025c67eb25ce5cda634518d308d4e9 F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 -F src/os_kv.c 889c6f9b59702859f9041549845769bae2cbd6b59cf15bd7904551a676b85533 +F src/os_kv.c d4909b439043183f9b6aec65528a7433cee49d41cca95b9a3a4c8fb6bbedc0c2 F src/os_setup.h 0711dbc4678f3ac52d7fe736951b6384a0615387c4ba5135a4764e4e31f4b6a6 F src/os_unix.c 0fa91925f0b8831fc0156a9c04d39d86f85baf9eef66c98712395e1715cb75cc F src/os_win.c e9454cb141908e8eef2102180bad353a36480612d5b736e4c2bd5777d9b25a34 @@ -2004,8 +2004,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 878cc93e779c3857adc711f212520f007256274fcd3f6f3d72c754a937fcd198 -R ea387427fef090c92e0fdcd9a6ab25a2 +P 1c5aeee45564c14e7e2e7730f3f52106339ea3148fb5aa786fa767a35db46f51 +R 06fb792046d4c535864a12af9a1a5d61 U stephan -Z d8eaf9989333b333239c3719fa4ba2d7 +Z 125d6637967f22bfb5b9fb3681eda03b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8dbaf37dda..445b7e7e25 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1c5aeee45564c14e7e2e7730f3f52106339ea3148fb5aa786fa767a35db46f51 \ No newline at end of file +e334449912d5176e355d02024a07ed867741f71c9d10ce6744ca800414bf3eeb \ No newline at end of file diff --git a/src/os_kv.c b/src/os_kv.c index 62214a13ad..17d4c254e1 100644 --- a/src/os_kv.c +++ b/src/os_kv.c @@ -351,7 +351,7 @@ int sqlite3__wasm_emjs_kvvfs(int whichOp){ switch( whichOp ){ case 0: break; case 1: - kvstorageWrite(zClass, zKey, "world"); + rc = kvstorageWrite(zClass, zKey, "world"); break; case 2: { char buffer[128] = {0}; @@ -602,7 +602,7 @@ static void kvvfsDecodeJournal( const char *zTxt, /* Text encoding. Zero-terminated */ int nTxt /* Bytes in zTxt, excluding zero terminator */ ){ - unsigned int n = 0; + unsigned int n; int c, i, mult; i = 0; mult = 1; @@ -634,10 +634,10 @@ static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){ kvstorageRead(pFile->zClass, "sz", zData, sizeof(zData)-1); return strtoll(zData, 0, 0); } -static void kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){ +static int kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){ char zData[50]; sqlite3_snprintf(sizeof(zData), zData, "%lld", sz); - kvstorageWrite(pFile->zClass, "sz", zData); + return kvstorageWrite(pFile->zClass, "sz", zData); } /****** sqlite3_io_methods methods ******************************************/ @@ -788,7 +788,9 @@ static int kvvfsWriteDb( pgno = 1 + iOfst/iAmt; sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); kvvfsEncode(zBuf, iAmt, aData); - kvstorageWrite(pFile->zClass, zKey, aData); + if( kvstorageWrite(pFile->zClass, zKey, aData) ){ + return SQLITE_IOERR; + } if( iOfst+iAmt > pFile->szDb ){ pFile->szDb = iOfst + iAmt; } @@ -825,8 +827,7 @@ static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){ pgno++; } pFile->szDb = size; - kvvfsWriteFileSize(pFile, size); - return SQLITE_OK; + return kvvfsWriteFileSize(pFile, size) ? SQLITE_IOERR : SQLITE_OK; } return SQLITE_IOERR; } @@ -854,17 +855,18 @@ static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){ }while( n>0 ); zOut[i++] = ' '; kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]); - kvstorageWrite(pFile->zClass, "jrnl", zOut); + i = kvstorageWrite(pFile->zClass, "jrnl", zOut); sqlite3_free(zOut); - return SQLITE_OK; + return i ? SQLITE_IOERR : SQLITE_OK; } static int kvvfsSyncDb(sqlite3_file *pProtoFile, int flags){ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + int rc = SQLITE_OK; SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass)); - if( pFile->szDb>0 ){ - kvvfsWriteFileSize(pFile, pFile->szDb); + if( pFile->szDb>0 && 0!=kvvfsWriteFileSize(pFile, pFile->szDb) ){ + rc = SQLITE_IOERR; } - return SQLITE_OK; + return rc; } /* From 58bcfe075dcaf4bc94f7f1d56ce3a4b3ed230e22 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 16 Sep 2022 02:28:03 +0000 Subject: [PATCH 113/428] Remove --nosync flag from speedtest1-kvvfs.html, as that seems to be the trigger for the 'no such table: z1' error. That app now warns if that flag is passed to it via URL args. FossilOrigin-Name: ad0677e8abcc077636d1cf1d8485be4943506382581edf832e6b8a2021560040 --- ext/wasm/speedtest1-kvvfs.html | 7 +++++-- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ext/wasm/speedtest1-kvvfs.html b/ext/wasm/speedtest1-kvvfs.html index 272425262b..080f7e4e18 100644 --- a/ext/wasm/speedtest1-kvvfs.html +++ b/ext/wasm/speedtest1-kvvfs.html @@ -107,9 +107,9 @@ argv.push( "--singlethread", "--nomutex", - "--nosync", + //"--nosync", "--nomemstat" - //"--sqlonly" + // ,"--sqlonly" ); //argv.push("--memdb" /* note that memdb trumps the filename arg */); } @@ -118,6 +118,9 @@ if(argv.indexOf('--memdb')>=0){ log2('error',"WARNING: --memdb flag trumps db filename."); } + if(argv.indexOf('--nosync')>=0){ + log2('error',"WARNING: --nosync flag is known to cause this test to fail."); + } console.log("argv =",argv); // These log messages are not emitted to the UI until after main() returns. Fixing that // requires moving the main() call and related cleanup into a timeout handler. diff --git a/manifest b/manifest index b9e43a7389..37ab517eef 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sreturn\svalue\schecks\sto\skvstorageWrite(),\snecessary\sfor\srecognizing\sstorage-full\serrors\sin\sthe\sJS\sbinding.\sspeedtest1-kvvfs.html\sruns\ssuccessfully\son\slocalStorage\swith\s--size\s1\sto\s6\sand\sstarts\sfailing\swith\sstorage\sshortage\sat\s--size\s7. -D 2022-09-16T01:05:19.939 +C Remove\s--nosync\sflag\sfrom\sspeedtest1-kvvfs.html,\sas\sthat\sseems\sto\sbe\sthe\strigger\sfor\sthe\s'no\ssuch\stable:\sz1'\serror.\sThat\sapp\snow\swarns\sif\sthat\sflag\sis\spassed\sto\sit\svia\sURL\sargs. +D 2022-09-16T02:28:03.733 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -514,7 +514,7 @@ F ext/wasm/scratchpad-opfs-worker.js cf6c4554d3b099c1a50013e50d19b3dc60e183511b4 F ext/wasm/scratchpad-opfs-worker2.js 2424d7d7b8801fc143f6540fbdd8a96f3f2e5b811f0f545714d06147ccce58bf F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06 F ext/wasm/scratchpad-wasmfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e -F ext/wasm/speedtest1-kvvfs.html c9cb752f9c81cdcf9ccdc09146bd8c9c77970ed44ab10908ed15f1cce95c0f8d +F ext/wasm/speedtest1-kvvfs.html ad08e2018c67cde7c459f692122a7d675b54a3709726c094ecbeed230f36abd3 F ext/wasm/speedtest1-wasmfs.html 6a67a6812f03a2058eb5c6ad0c8dea4bf749d0160ed9d6b826dabe7b766c3cf7 F ext/wasm/speedtest1-worker.html d8881ae802d15fb8adb94049265173e99f350e07e1d4e6f9e1cbd8969fe63a04 F ext/wasm/speedtest1-worker.js fb5d282c0b8aed18daf41c57f768cbf434f8137dbff707d53dcedcd7d4cb60ef @@ -2026,8 +2026,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 00ee49a3a2c148480f614e49a0ee5b35a255758c0a53693f0b464b31e7a4045b -R a6e9c244c29b55941f4ad5b4e78621c2 +P 13839759f8f45e4eb0aa6a5052801f3964d5b2dc2e427e5e91ee6692176381eb +R bad0de454d5232b2bfe356090d3af869 U stephan -Z daf2b8c0bbcd601af0a5bcd00e934942 +Z 949ebd6be72bc73feff93f2475059791 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b3a08b5d95..ca8b17d577 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -13839759f8f45e4eb0aa6a5052801f3964d5b2dc2e427e5e91ee6692176381eb \ No newline at end of file +ad0677e8abcc077636d1cf1d8485be4943506382581edf832e6b8a2021560040 \ No newline at end of file From 57db2174d53b713d3bdeb38fa05d9eb1580ee9c0 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 16 Sep 2022 02:30:49 +0000 Subject: [PATCH 114/428] Add batch-runner-kvvfs.html, a kvvfs-specific build of batch-runner.html. Reduce the speedtest1 --size X value for the batch list generation to 5 so that the kvvfs batch runner can handle it. FossilOrigin-Name: d8df25920a047d5cf2093cc6233128c5e6057a9104d0c4397e643645bd646fe1 --- ext/wasm/GNUmakefile | 3 +- ext/wasm/batch-runner-kvvfs.html | 65 ++++++++++++++++++++++++++++++++ ext/wasm/batch-runner.js | 23 +++++++---- ext/wasm/index.html | 1 + manifest | 17 +++++---- manifest.uuid | 2 +- 6 files changed, 93 insertions(+), 18 deletions(-) create mode 100644 ext/wasm/batch-runner-kvvfs.html diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index fe3c7a61f8..18ca4ef3ea 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -308,7 +308,8 @@ dir.sql := sql speedtest1 := ../../speedtest1 speedtest1.c := ../../test/speedtest1.c speedtest1.sql := $(dir.sql)/speedtest1.sql -speedtest1.cliflags := --size 100 +speedtest1.cliflags := --size 5 --big-transactions +# --------------------^^^^^^^^ small size is needed for batch-runner-kvvfs.html $(speedtest1): $(MAKE) -C ../.. speedtest1 $(speedtest1.sql): $(speedtest1) $(MAKEFILE) diff --git a/ext/wasm/batch-runner-kvvfs.html b/ext/wasm/batch-runner-kvvfs.html new file mode 100644 index 0000000000..420bdf4f20 --- /dev/null +++ b/ext/wasm/batch-runner-kvvfs.html @@ -0,0 +1,65 @@ + + + + + + + + + sqlite3-api kvvfs batch SQL runner + + +
    sqlite3-api kvvfs batch SQL runner
    + +
    +
    +
    Initializing app...
    +
    + 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. +
    +
    +
    Downloading...
    +
    + +
    +

    + This page is for batch-running extracts from the output + of speedtest1 --script, as well as other standalone SQL + scripts. This variant runs with KVVFS and + has tiny size limits. +

    +

    ACHTUNG: this file requires a generated input list + file. Run "make batch" from this directory to generate it. +

    +

    WARNING: if the WASMFS/OPFS layer crashes, this page may + become completely unresponsive and need to be closed and + reloaded to recover. +

    +
    +
    + + + + + + + + + + + +
    +
    + +
    + + + + + + diff --git a/ext/wasm/batch-runner.js b/ext/wasm/batch-runner.js index 9964d747c7..7a90ddd13f 100644 --- a/ext/wasm/batch-runner.js +++ b/ext/wasm/batch-runner.js @@ -356,17 +356,24 @@ this.logHtml("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); this.logHtml("WASM heap size =",wasm.heap8().length); this.loadSqlList(); - const pDir = 1 ? '' : capi.sqlite3_web_persistent_dir(); - const dbFile = pDir ? pDir+"/speedtest.db" : ( - sqlite3.capi.sqlite3_vfs_find('kvvfs') ? 'local' : ':memory:' - ); - this.clearStorage(); - if(pDir){ - logHtml("Using persistent storage:",dbFile); + let pDir, dbFile; + if(sqlite3.capi.sqlite3_vfs_find('kvvfs')){ + dbFile = 1 ? 'local' : 'session'; + this.logHtml("Using KVVFS storage:",dbFile); }else{ + pDir = capi.sqlite3_web_persistent_dir(); + if(pDir){ + dbFile = pDir+"/speedtest.db"; + this.logHtml("Using persistent storage:",dbFile); + }else{ + dbFile = ':memory:'; + this.logHtml("Using",dbFile,"storage."); + } + } + if(!pDir){ document.querySelector('#warn-opfs').remove(); } - this.openDb(dbFile, !!pDir); + this.openDb(dbFile, true); const who = this; const eReverseLogNotice = document.querySelector('#reverse-log-notice'); if(this.e.cbReverseLog.checked){ diff --git a/ext/wasm/index.html b/ext/wasm/index.html index 383045ea2c..20c96c8cf0 100644 --- a/ext/wasm/index.html +++ b/ext/wasm/index.html @@ -37,6 +37,7 @@ tests for the Promise-based wrapper of the Worker-based API.
  • demo-oo1: demonstration of the OO API #1.
  • batch-runner: runs batches of SQL exported from speedtest1.
  • +
  • batch-runner-kvvfs: KVVFS-specific variant of batch-runner.html.
  • speedtest1: a main-thread WASM build of speedtest1.
  • speedtest1-worker: an interactive Worker-thread variant of speedtest1.
  • speedtest1-wasmfs: a variant of speedtest1 built solely for the wasmfs/opfs feature.
  • diff --git a/manifest b/manifest index 37ab517eef..3803dbcdfe 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\s--nosync\sflag\sfrom\sspeedtest1-kvvfs.html,\sas\sthat\sseems\sto\sbe\sthe\strigger\sfor\sthe\s'no\ssuch\stable:\sz1'\serror.\sThat\sapp\snow\swarns\sif\sthat\sflag\sis\spassed\sto\sit\svia\sURL\sargs. -D 2022-09-16T02:28:03.733 +C Add\sbatch-runner-kvvfs.html,\sa\skvvfs-specific\sbuild\sof\sbatch-runner.html.\sReduce\sthe\sspeedtest1\s--size\sX\svalue\sfor\sthe\sbatch\slist\sgeneration\sto\s5\sso\sthat\sthe\skvvfs\sbatch\srunner\scan\shandle\sit. +D 2022-09-16T02:30:49.153 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile bec8b98f00c81561215bd8adff9870a30508fd6813bccf1b821ddf073446525e +F ext/wasm/GNUmakefile ab4a43ee7aaa5abf8e30f02cedfb803a18f632bb6554e800d96616050ccab37d F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 150a793a47205b8009ac934f3b6d6ebf67b965c072339aaa25ce808a19e116cc F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -489,8 +489,9 @@ F ext/wasm/api/sqlite3-api-prologue.js 7aff2d9b110d5a6e70dbf5fe44189d983d7886a46 F ext/wasm/api/sqlite3-api-worker1.js d33062afa045fd4be01ba4abc266801807472558b862b30056211b00c9c347b4 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 4130e2df9587f4e4c3afc04c3549d682c8a5c0cfe5b22819a0a86edb7f01b9bd +F ext/wasm/batch-runner-kvvfs.html ef3b2f553abad4f17a2a29ce6526023793a88e8597b7a24b8c7855a030b90a16 F ext/wasm/batch-runner.html 2857a6db7292ac83d1581af865d643fd34235db2df830d10b43b01388c599e04 -F ext/wasm/batch-runner.js fb6a338aeef509f181f499700a8595bc578bbc54ef832e0cda7e7e7c10b90a18 +F ext/wasm/batch-runner.js 6f5b86e0b5519a9a941d9f17ee9c5ecdc63f452f157602fe7fdf87f6275a2b49 F ext/wasm/common/SqliteTestUtil.js 529161a624265ba84271a52db58da022649832fa1c71309fb1e02cc037327a2b F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 3a5143699c2b73a85b962271e1a9b3241b30d90e30d895e4f55665e648572962 @@ -501,7 +502,7 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 -F ext/wasm/index.html e251800a64cf13e731ddec5a86ca4a299dcc4c1fafafd87658ccca3c538dd843 +F ext/wasm/index.html 095b9a8cee9aac2654c23686ead22f3452b89d581fb41d34d47b6548546b5365 F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f @@ -2026,8 +2027,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 13839759f8f45e4eb0aa6a5052801f3964d5b2dc2e427e5e91ee6692176381eb -R bad0de454d5232b2bfe356090d3af869 +P ad0677e8abcc077636d1cf1d8485be4943506382581edf832e6b8a2021560040 +R 6eaf6290c7f1da025383bc24b7d47eee U stephan -Z 949ebd6be72bc73feff93f2475059791 +Z 5b7d05db593e0eeb6d33327ff3472ed8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ca8b17d577..34cc1ee971 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ad0677e8abcc077636d1cf1d8485be4943506382581edf832e6b8a2021560040 \ No newline at end of file +d8df25920a047d5cf2093cc6233128c5e6057a9104d0c4397e643645bd646fe1 \ No newline at end of file From 84e50767871d64ed2e9ffcf7dbee49d97746ce6f Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 16 Sep 2022 11:37:01 +0000 Subject: [PATCH 115/428] Fix os_kv.c so that it uses SQLITE_FCNTL_SYNC and hence works even with PRAGMA synchronous=OFF. FossilOrigin-Name: 21915af560b111aeeaee751790356151a5f063c2fc703dd4b35b22dc393409fb --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/os_kv.c | 31 ++++++++++++++++++++----------- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/manifest b/manifest index da3fab544e..57b3e2ecad 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Pull\sthe\ssrc/os_kv.c\spart\sof\s[13839759f8f4]\sinto\sthe\skv-vfs\sbranch. -D 2022-09-16T01:08:06.279 +C Fix\sos_kv.c\sso\sthat\sit\suses\sSQLITE_FCNTL_SYNC\sand\shence\nworks\seven\swith\sPRAGMA\ssynchronous=OFF. +D 2022-09-16T11:37:01.212 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -574,7 +574,7 @@ F src/notify.c 89a97dc854c3aa62ad5f384ef50c5a4a11d70fcc69f86de3e991573421130ed6 F src/os.c 0eb831ba3575af5277e47f4edd14fdfc90025c67eb25ce5cda634518d308d4e9 F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 -F src/os_kv.c d4909b439043183f9b6aec65528a7433cee49d41cca95b9a3a4c8fb6bbedc0c2 +F src/os_kv.c 554a2c109f8810b743af2eed4ba732d18dfdbc4d073e3a9bd8b8e828215a9692 F src/os_setup.h 0711dbc4678f3ac52d7fe736951b6384a0615387c4ba5135a4764e4e31f4b6a6 F src/os_unix.c 0fa91925f0b8831fc0156a9c04d39d86f85baf9eef66c98712395e1715cb75cc F src/os_win.c e9454cb141908e8eef2102180bad353a36480612d5b736e4c2bd5777d9b25a34 @@ -2004,8 +2004,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 1c5aeee45564c14e7e2e7730f3f52106339ea3148fb5aa786fa767a35db46f51 -R 06fb792046d4c535864a12af9a1a5d61 -U stephan -Z 125d6637967f22bfb5b9fb3681eda03b +P e334449912d5176e355d02024a07ed867741f71c9d10ce6744ca800414bf3eeb +R a55522768af3f3b5d07199ad1dd4c789 +U drh +Z d50c7a05ab573e361ead585b4e643b32 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 445b7e7e25..c9139459b8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e334449912d5176e355d02024a07ed867741f71c9d10ce6744ca800414bf3eeb \ No newline at end of file +21915af560b111aeeaee751790356151a5f063c2fc703dd4b35b22dc393409fb \ No newline at end of file diff --git a/src/os_kv.c b/src/os_kv.c index 17d4c254e1..b99f5574c7 100644 --- a/src/os_kv.c +++ b/src/os_kv.c @@ -71,7 +71,8 @@ static int kvvfsFileSizeJrnl(sqlite3_file*, sqlite3_int64 *pSize); static int kvvfsLock(sqlite3_file*, int); static int kvvfsUnlock(sqlite3_file*, int); static int kvvfsCheckReservedLock(sqlite3_file*, int *pResOut); -static int kvvfsFileControl(sqlite3_file*, int op, void *pArg); +static int kvvfsFileControlDb(sqlite3_file*, int op, void *pArg); +static int kvvfsFileControlJrnl(sqlite3_file*, int op, void *pArg); static int kvvfsSectorSize(sqlite3_file*); static int kvvfsDeviceCharacteristics(sqlite3_file*); @@ -123,7 +124,7 @@ static sqlite3_io_methods kvvfs_db_io_methods = { kvvfsLock, /* xLock */ kvvfsUnlock, /* xUnlock */ kvvfsCheckReservedLock, /* xCheckReservedLock */ - kvvfsFileControl, /* xFileControl */ + kvvfsFileControlDb, /* xFileControl */ kvvfsSectorSize, /* xSectorSize */ kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ 0, /* xShmMap */ @@ -147,7 +148,7 @@ static sqlite3_io_methods kvvfs_jrnl_io_methods = { kvvfsLock, /* xLock */ kvvfsUnlock, /* xUnlock */ kvvfsCheckReservedLock, /* xCheckReservedLock */ - kvvfsFileControl, /* xFileControl */ + kvvfsFileControlJrnl, /* xFileControl */ kvvfsSectorSize, /* xSectorSize */ kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ 0, /* xShmMap */ @@ -860,13 +861,7 @@ static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){ return i ? SQLITE_IOERR : SQLITE_OK; } static int kvvfsSyncDb(sqlite3_file *pProtoFile, int flags){ - KVVfsFile *pFile = (KVVfsFile *)pProtoFile; - int rc = SQLITE_OK; - SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass)); - if( pFile->szDb>0 && 0!=kvvfsWriteFileSize(pFile, pFile->szDb) ){ - rc = SQLITE_IOERR; - } - return rc; + return SQLITE_OK; } /* @@ -928,7 +923,21 @@ static int kvvfsCheckReservedLock(sqlite3_file *pProtoFile, int *pResOut){ /* ** File control method. For custom operations on an kvvfs-file. */ -static int kvvfsFileControl(sqlite3_file *pProtoFile, int op, void *pArg){ +static int kvvfsFileControlJrnl(sqlite3_file *pProtoFile, int op, void *pArg){ + SQLITE_KV_LOG(("xFileControl(%d) on journal\n", op)); + return SQLITE_NOTFOUND; +} +static int kvvfsFileControlDb(sqlite3_file *pProtoFile, int op, void *pArg){ + SQLITE_KV_LOG(("xFileControl(%d) on database\n", op)); + if( op==SQLITE_FCNTL_SYNC ){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + int rc = SQLITE_OK; + SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass)); + if( pFile->szDb>0 && 0!=kvvfsWriteFileSize(pFile, pFile->szDb) ){ + rc = SQLITE_IOERR; + } + return rc; + } return SQLITE_NOTFOUND; } From 81439a07f01ddb8e72b6fd3c4d0a0f6ca345d38c Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 16 Sep 2022 12:49:45 +0000 Subject: [PATCH 116/428] When calculating kvvfs sessionStorage/localStorage size, multiply it by 2 to account for JS using 16-bit characters. FossilOrigin-Name: 52d1b185b9f6cee1eb3dec436f47e0f52e4621a127abfad8c27f92fd78147889 --- ext/wasm/api/sqlite3-api-prologue.js | 4 ++-- ext/wasm/speedtest1-kvvfs.html | 2 +- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 3d6d4c92b5..1fc533da62 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -818,7 +818,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( string (the default) then both are counted. Only storage keys which match the pattern used by kvvfs are counted. The returned value is the "length" value of every matching key and value, - noting that the kvvf uses only ASCII keys and values. + noting that JavaScript stores each character in 2 bytes. Note that the returned size is not authoritative from the perspective of how much data can fit into localStorage and @@ -839,7 +839,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( } } }); - return sz; + return sz * 2 /* because JS uses UC16 encoding */; }; /** diff --git a/ext/wasm/speedtest1-kvvfs.html b/ext/wasm/speedtest1-kvvfs.html index 9bb387643b..82d32f4fe0 100644 --- a/ext/wasm/speedtest1-kvvfs.html +++ b/ext/wasm/speedtest1-kvvfs.html @@ -77,7 +77,7 @@ } } }); - return sz; + return sz * 2 /* for 16-bit-char encoding */; }; const clearStorage = function(){ sessionStorage.clear(); diff --git a/manifest b/manifest index a4dec59160..f946cd3989 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\skv-vfs\sbranch\sinto\sfiddle-opfs\sbranch\sfor\s[21915af560b1|synchronous=off\sfix].\sRemove\ssome\sduplicate\sdebug\soutput\sin\sOPFS\stest\scode. -D 2022-09-16T11:45:09.066 +C When\scalculating\skvvfs\ssessionStorage/localStorage\ssize,\smultiply\sit\sby\s2\sto\saccount\sfor\sJS\susing\s16-bit\scharacters. +D 2022-09-16T12:49:45.564 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -485,7 +485,7 @@ F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 F ext/wasm/api/sqlite3-api-oo1.js d7526517f7ad3f6bda16ad66d373bbb71b43168deef7af60eda5c9fe873d1387 F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 -F ext/wasm/api/sqlite3-api-prologue.js 7aff2d9b110d5a6e70dbf5fe44189d983d7886a465ad156176f4ad290447c97b +F ext/wasm/api/sqlite3-api-prologue.js 48ebca4ae340b0242d4f39bbded01bd0588393c8023628be1c454b4db6f7bd6e F ext/wasm/api/sqlite3-api-worker1.js d33062afa045fd4be01ba4abc266801807472558b862b30056211b00c9c347b4 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 4130e2df9587f4e4c3afc04c3549d682c8a5c0cfe5b22819a0a86edb7f01b9bd @@ -515,7 +515,7 @@ F ext/wasm/scratchpad-opfs-worker.js cf6c4554d3b099c1a50013e50d19b3dc60e183511b4 F ext/wasm/scratchpad-opfs-worker2.js 8c980370bbd5a262d96af8627c443936e11b87d0263a02123769d5953fc146da F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06 F ext/wasm/scratchpad-wasmfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e -F ext/wasm/speedtest1-kvvfs.html 6e6e918d44b819a9ea1a146f260bd69022bdccd854439ee2d8c646f5073c2ea8 +F ext/wasm/speedtest1-kvvfs.html 20f47314ea1b892cfb9248d63e2514573be089d7ab1949dddadd4fed761e07a9 F ext/wasm/speedtest1-wasmfs.html 6a67a6812f03a2058eb5c6ad0c8dea4bf749d0160ed9d6b826dabe7b766c3cf7 F ext/wasm/speedtest1-worker.html d8881ae802d15fb8adb94049265173e99f350e07e1d4e6f9e1cbd8969fe63a04 F ext/wasm/speedtest1-worker.js fb5d282c0b8aed18daf41c57f768cbf434f8137dbff707d53dcedcd7d4cb60ef @@ -2027,8 +2027,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d8df25920a047d5cf2093cc6233128c5e6057a9104d0c4397e643645bd646fe1 21915af560b111aeeaee751790356151a5f063c2fc703dd4b35b22dc393409fb -R 9d036c771fee3f8c011441c0aa641532 +P 13899bb98c80525276d2484598b94e4206358f243f06d45c02700024f7e226fd +R 9d9ce1010dbc2786b3b027fc9d4d0a5f U stephan -Z 6c79830061a07791b98ec477a66ca0ad +Z a004dca4566819b88df76b8693bd15c3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5aecdc9f0f..ea2d80a593 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -13899bb98c80525276d2484598b94e4206358f243f06d45c02700024f7e226fd \ No newline at end of file +52d1b185b9f6cee1eb3dec436f47e0f52e4621a127abfad8c27f92fd78147889 \ No newline at end of file From 756440febc24d57d6d85e343b969678c234b03bb Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 16 Sep 2022 15:19:04 +0000 Subject: [PATCH 117/428] Corrections to the xRead method for databases in the os_kv.c VFS so that it correctly reads the header, and thus avoids unnecessary cache flushes. This changes also includes an optimization to the header read logic. FossilOrigin-Name: ef54961ce69fddb4cfeeff0860288de2858a6f7a5aa396691e8e99933eb9af54 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/os_kv.c | 8 +++++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 57b3e2ecad..62feced741 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sos_kv.c\sso\sthat\sit\suses\sSQLITE_FCNTL_SYNC\sand\shence\nworks\seven\swith\sPRAGMA\ssynchronous=OFF. -D 2022-09-16T11:37:01.212 +C Corrections\sto\sthe\sxRead\smethod\sfor\sdatabases\sin\sthe\sos_kv.c\sVFS\sso\sthat\sit\ncorrectly\sreads\sthe\sheader,\sand\sthus\savoids\sunnecessary\scache\sflushes.\s\sThis\nchanges\salso\sincludes\san\soptimization\sto\sthe\sheader\sread\slogic. +D 2022-09-16T15:19:04.113 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -574,7 +574,7 @@ F src/notify.c 89a97dc854c3aa62ad5f384ef50c5a4a11d70fcc69f86de3e991573421130ed6 F src/os.c 0eb831ba3575af5277e47f4edd14fdfc90025c67eb25ce5cda634518d308d4e9 F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 -F src/os_kv.c 554a2c109f8810b743af2eed4ba732d18dfdbc4d073e3a9bd8b8e828215a9692 +F src/os_kv.c 3a2d319988df9da8f3a4d0c753a1492542bd3017ff7ae5d07814460eac6b0b19 F src/os_setup.h 0711dbc4678f3ac52d7fe736951b6384a0615387c4ba5135a4764e4e31f4b6a6 F src/os_unix.c 0fa91925f0b8831fc0156a9c04d39d86f85baf9eef66c98712395e1715cb75cc F src/os_win.c e9454cb141908e8eef2102180bad353a36480612d5b736e4c2bd5777d9b25a34 @@ -2004,8 +2004,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e334449912d5176e355d02024a07ed867741f71c9d10ce6744ca800414bf3eeb -R a55522768af3f3b5d07199ad1dd4c789 +P 21915af560b111aeeaee751790356151a5f063c2fc703dd4b35b22dc393409fb +R 64099072882bb903603d527810cdf234 U drh -Z d50c7a05ab573e361ead585b4e643b32 +Z 33c23cb0033c7cdf863f626ff2ea91a9 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c9139459b8..54e92cfb4b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -21915af560b111aeeaee751790356151a5f063c2fc703dd4b35b22dc393409fb \ No newline at end of file +ef54961ce69fddb4cfeeff0860288de2858a6f7a5aa396691e8e99933eb9af54 \ No newline at end of file diff --git a/src/os_kv.c b/src/os_kv.c index b99f5574c7..db95a305b7 100644 --- a/src/os_kv.c +++ b/src/os_kv.c @@ -700,7 +700,7 @@ static int kvvfsReadDb( unsigned int pgno; int got, n; char zKey[30]; - char aData[131073]; + char aData[133073]; assert( iOfst>=0 ); assert( iAmt>=0 ); SQLITE_KV_LOG(("xRead('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); @@ -723,9 +723,11 @@ static int kvvfsReadDb( }else{ aData[got] = 0; if( iOfst+iAmt<512 ){ - n = kvvfsDecode(aData, &aData[1000], 1000); + int k = iOfst+iAmt; + aData[k*2] = 0; + n = kvvfsDecode(aData, &aData[2000], sizeof(aData)-2000); if( n>=iOfst+iAmt ){ - memcpy(zBuf, &aData[1000+iOfst], iAmt); + memcpy(zBuf, &aData[2000+iOfst], iAmt); n = iAmt; }else{ n = 0; From 132a87baa366bcda8b26e0e4e56c6abba4c5d453 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 17 Sep 2022 15:08:22 +0000 Subject: [PATCH 118/428] Add initial bits of an experimental async-impl-via-synchronous-interface proxy intended to marshal OPFS via sqlite3_vfs API. FossilOrigin-Name: 38da059b472415da52f57de7332fbeb8a91e3add1f4be3ff9c1924b52672f77c --- ext/wasm/api/sqlite3-api-opfs.js | 7 +- ext/wasm/index.html | 8 +- ext/wasm/sqlite3-opfs-async-proxy.js | 286 +++++++++++++++++++++++++++ ext/wasm/x-sync-async.html | 28 +++ ext/wasm/x-sync-async.js | 133 +++++++++++++ manifest | 17 +- manifest.uuid | 2 +- 7 files changed, 469 insertions(+), 12 deletions(-) create mode 100644 ext/wasm/sqlite3-opfs-async-proxy.js create mode 100644 ext/wasm/x-sync-async.html create mode 100644 ext/wasm/x-sync-async.js diff --git a/ext/wasm/api/sqlite3-api-opfs.js b/ext/wasm/api/sqlite3-api-opfs.js index 693432b35a..3faf956c72 100644 --- a/ext/wasm/api/sqlite3-api-opfs.js +++ b/ext/wasm/api/sqlite3-api-opfs.js @@ -34,11 +34,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const warn = console.warn.bind(console), error = console.error.bind(console); - if(!self.importScripts || !self.FileSystemFileHandle){ - //|| !self.FileSystemFileHandle.prototype.createSyncAccessHandle){ - // ^^^ sync API is not required with WASMFS/OPFS backend. + if(self.window===self || !self.importScripts || !self.FileSystemFileHandle + || !self.FileSystemFileHandle.prototype.createSyncAccessHandle){ warn("OPFS is not available in this environment."); return; + }else if(!navigator.storage.getDirectory){ + warn("The OPFS VFS requires navigator.storage.getDirectory."); }else if(!sqlite3.capi.wasm.bigIntEnabled){ error("OPFS requires BigInt support but sqlite3.capi.wasm.bigIntEnabled is false."); return; diff --git a/ext/wasm/index.html b/ext/wasm/index.html index 20c96c8cf0..a1cc194854 100644 --- a/ext/wasm/index.html +++ b/ext/wasm/index.html @@ -49,7 +49,13 @@ reminder: we cannot currently (2022-09-15) load WASMFS in a worker due to an Emscripten limitation.
  • scratchpad-opfs-worker: - experimenting with OPFS from a Worker thread (without WASMFS). + experimenting with OPFS from a Worker thread (without WASMFS).
  • +
  • x-sync-async is an + experiment in implementing a syncronous sqlite3 VFS proxy + for a fully synchronous backend interface (namely OPFS), using SharedArrayBuffer + and the Atomics APIs to regulate communication between the synchronous + interface and the async impl. +
  • diff --git a/ext/wasm/sqlite3-opfs-async-proxy.js b/ext/wasm/sqlite3-opfs-async-proxy.js new file mode 100644 index 0000000000..98e2688149 --- /dev/null +++ b/ext/wasm/sqlite3-opfs-async-proxy.js @@ -0,0 +1,286 @@ +/* + 2022-09-16 + + 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 EXTREMELY INCOMPLETE and UNDER CONSTRUCTION experiment for OPFS: a + Worker which manages asynchronous OPFS handles on behalf of a + synchronous API which controls it via a combination of Worker + messages, SharedArrayBuffer, and Atomics. + + Highly indebted to: + + https://github.com/rhashimoto/wa-sqlite/blob/master/src/examples/OriginPrivateFileSystemVFS.js + + for demonstrating how to use the OPFS APIs. +*/ +'use strict'; +(function(){ + const toss = function(...args){throw new Error(args.join(' '))}; + if(self.window === self){ + toss("This code cannot run from the main thread.", + "Load it as a Worker from a separate Worker."); + }else if(!navigator.storage.getDirectory){ + toss("This API requires navigator.storage.getDirectory."); + } + const logPrefix = "OPFS worker:"; + const log = (...args)=>{ + console.log(logPrefix,...args); + }; + const warn = (...args)=>{ + console.warn(logPrefix,...args); + }; + const error = (...args)=>{ + console.error(logPrefix,...args); + }; + + warn("This file is very much experimental and under construction.",self.location.pathname); + const wMsg = (type,payload)=>postMessage({type,payload}); + + const state = Object.create(null); + /*state.opSab; + state.sabIO; + state.opBuf; + state.opIds; + state.rootDir;*/ + /** + Map of sqlite3_file pointers (integers) to metadata related to a + given OPFS file handles. The pointers are, in this side of the + interface, opaque file handle IDs provided by the synchronous + part of this constellation. Each value is an object with a structure + demonstrated in the xOpen() impl. + */ + state.openFiles = Object.create(null); + + /** + Map of dir names to FileSystemDirectoryHandle objects. + */ + state.dirCache = new Map; + + const __splitPath = (absFilename)=>{ + const a = absFilename.split('/').filter((v)=>!!v); + return [a, a.pop()]; + }; + /** + Takes the absolute path to a filesystem element. Returns an array + of [handleOfContainingDir, filename]. If the 2nd argument is + truthy then each directory element leading to the file is created + along the way. Throws if any creation or resolution fails. + */ + const getDirForPath = async function f(absFilename, createDirs = false){ + const url = new URL( + absFilename, 'file://xyz' + ) /* use URL to resolve path pieces such as a/../b */; + const [path, filename] = __splitPath(url.pathname); + const allDirs = path.join('/'); + let dh = state.dirCache.get(allDirs); + if(!dh){ + dh = state.rootDir; + for(const dirName of path){ + if(dirName){ + dh = await dh.getDirectoryHandle(dirName, {create: !!createDirs}); + } + } + state.dirCache.set(allDirs, dh); + } + return [dh, filename]; + }; + + + /** + Generates a random ASCII string len characters long, intended for + use as a temporary file name. + */ + const randomFilename = function f(len=16){ + if(!f._chars){ + f._chars = "abcdefghijklmnopqrstuvwxyz"+ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ + "012346789"; + f._n = f._chars.length; + } + const a = []; + let i = 0; + for( ; i < len; ++i){ + const ndx = Math.random() * (f._n * 64) % f._n | 0; + a[i] = f._chars[ndx]; + } + return a.join(''); + }; + + const storeAndNotify = (opName, value)=>{ + log(opName+"() is notify()ing w/ value:",value); + Atomics.store(state.opBuf, state.opIds[opName], value); + Atomics.notify(state.opBuf, state.opIds[opName]); + }; + + const isInt32 = function(n){ + return ('bigint'!==typeof n /*TypeError: can't convert BigInt to number*/) + && !!(n===(n|0) && n<=2147483647 && n>=-2147483648); + }; + const affirm32Bits = function(n){ + return isInt32(n) || toss("Number is too large (>31 bits):",n); + }; + + const ioMethods = { + xAccess: async function({filename, exists, readWrite}){ + log("xAccess(",arguments,")"); + const rc = 1; + storeAndNotify('xAccess', rc); + }, + xClose: async function(fid){ + const opName = 'xClose'; + log(opName+"(",arguments[0],")"); + log("state.openFiles",state.openFiles); + const fh = state.openFiles[fid]; + if(fh){ + delete state.openFiles[fid]; + //await fh.close(); + if(fh.accessHandle) await fh.accessHandle.close(); + if(fh.deleteOnClose){ + try{ + await fh.dirHandle.removeEntry(fh.filenamePart); + } + catch(e){ + warn("Ignoring dirHandle.removeEntry() failure of",fh); + } + } + log("state.openFiles",state.openFiles); + storeAndNotify(opName, 0); + }else{ + storeAndNotify(opName, state.errCodes.NotFound); + } + }, + xDelete: async function(filename){ + log("xDelete(",arguments,")"); + storeAndNotify('xClose', 0); + }, + xFileSize: async function(fid){ + log("xFileSize(",arguments,")"); + const fh = state.openFiles[fid]; + const sz = await fh.getSize(); + affirm32Bits(sz); + storeAndNotify('xFileSize', sz | 0); + }, + xOpen: async function({ + fid/*sqlite3_file pointer*/, sab/*file-specific SharedArrayBuffer*/, + filename, + fileType = undefined /*mainDb, mainJournal, etc.*/, + create = false, readOnly = false, deleteOnClose = false, + }){ + const opName = 'xOpen'; + try{ + if(create) readOnly = false; + log(opName+"(",arguments[0],")"); + + let hDir, filenamePart, hFile; + try { + [hDir, filenamePart] = await getDirForPath(filename, !!create); + }catch(e){ + storeAndNotify(opName, state.errCodes.NotFound); + return; + } + hFile = await hDir.getFileHandle(filenamePart, {create: !!create}); + log(opName,"filenamePart =",filenamePart, 'hDir =',hDir); + const fobj = state.openFiles[fid] = Object.create(null); + fobj.filenameAbs = filename; + fobj.filenamePart = filenamePart; + fobj.dirHandle = hDir; + fobj.fileHandle = hFile; + fobj.accessHandle = undefined; + fobj.fileType = fileType; + fobj.sab = sab; + fobj.create = !!create; + fobj.readOnly = !!readOnly; + fobj.deleteOnClose = !!deleteOnClose; + + /** + wa-sqlite, at this point, grabs a SyncAccessHandle and + assigns it to the accessHandle prop of the file state + object, but it's unclear why it does that. + */ + storeAndNotify(opName, 0); + }catch(e){ + error(opName,e); + storeAndNotify(opName, state.errCodes.IO); + } + }, + xRead: async function({fid,n,offset}){ + log("xRead(",arguments,")"); + affirm32Bits(n + offset); + const fh = state.openFiles[fid]; + storeAndNotify('xRead',fid); + }, + xSleep: async function f({ms}){ + log("xSleep(",arguments[0],")"); + await new Promise((resolve)=>{ + setTimeout(()=>resolve(), ms); + }).finally(()=>storeAndNotify('xSleep',0)); + }, + xSync: async function({fid}){ + log("xSync(",arguments,")"); + const fh = state.openFiles[fid]; + await fh.flush(); + storeAndNotify('xSync',fid); + }, + xTruncate: async function({fid,size}){ + log("xTruncate(",arguments,")"); + affirm32Bits(size); + const fh = state.openFiles[fid]; + fh.truncate(size); + storeAndNotify('xTruncate',fid); + }, + xWrite: async function({fid,src,n,offset}){ + log("xWrite(",arguments,")"); + const fh = state.openFiles[fid]; + storeAndNotify('xWrite',fid); + } + }; + + const onReady = function(){ + self.onmessage = async function({data}){ + log("self.onmessage",data); + switch(data.type){ + case 'init':{ + const opt = data.payload; + state.opSab = opt.opSab; + state.opBuf = new Int32Array(state.opSab); + state.opIds = opt.opIds; + state.errCodes = opt.errCodes; + state.sq3Codes = opt.sq3Codes; + Object.keys(ioMethods).forEach((k)=>{ + if(!state.opIds[k]){ + toss("Maintenance required: missing state.opIds[",k,"]"); + } + }); + log("init state",state); + break; + } + default:{ + const m = ioMethods[data.type] || toss("Unknown message type:",data.type); + try { + await m(data.payload); + }catch(e){ + error("Error handling",data.type+"():",e); + storeAndNotify(data.type, -99); + } + break; + } + } + }; + wMsg('ready'); + }; + + navigator.storage.getDirectory().then(function(d){ + state.rootDir = d; + log("state.rootDir =",state.rootDir); + onReady(); + }); + +})(); diff --git a/ext/wasm/x-sync-async.html b/ext/wasm/x-sync-async.html new file mode 100644 index 0000000000..4b2e08a31f --- /dev/null +++ b/ext/wasm/x-sync-async.html @@ -0,0 +1,28 @@ + + + + + + + + + Async-behind-Sync experiment + + +
    Async-behind-Sync Experiment
    +
    This is an experiment in wrapping the + asynchronous OPFS APIs behind a fully synchronous proxy. It is + very much incomplete, under construction, and experimental. + See the dev console for all output. +
    +
    +
    + + + + + diff --git a/ext/wasm/x-sync-async.js b/ext/wasm/x-sync-async.js new file mode 100644 index 0000000000..fec7efa73f --- /dev/null +++ b/ext/wasm/x-sync-async.js @@ -0,0 +1,133 @@ +'use strict'; +const doAtomicsStuff = function(sqlite3){ + const logPrefix = "OPFS syncer:"; + const log = (...args)=>{ + console.log(logPrefix,...args); + }; + const warn = (...args)=>{ + console.warn(logPrefix,...args); + }; + const error = (...args)=>{ + console.error(logPrefix,...args); + }; + const W = new Worker("sqlite3-opfs-async-proxy.js"); + const wMsg = (type,payload)=>W.postMessage({type,payload}); + warn("This file is very much experimental and under construction.",self.location.pathname); + + /** + State which we send to the async-api Worker or share with it. + This object must initially contain only cloneable or sharable + objects. After the worker's "ready" message arrives, other types + of data may be added to it. + */ + const state = Object.create(null); + state.opIds = Object.create(null); + state.opIds.xAccess = 1; + state.opIds.xClose = 2; + state.opIds.xDelete = 3; + state.opIds.xFileSize = 4; + state.opIds.xOpen = 5; + state.opIds.xRead = 6; + state.opIds.xSync = 7; + state.opIds.xTruncate = 8; + state.opIds.xWrite = 9; + state.opIds.xSleep = 10; + state.opIds.xBlock = 99 /* to block worker while this code is still handling something */; + state.opSab = new SharedArrayBuffer(64); + state.fileBufferSize = 1024 * 65 /* 64k = max sqlite3 page size */; + /* TODO: use SQLITE_xxx err codes. */ + state.errCodes = Object.create(null); + state.errCodes.Error = -100; + state.errCodes.IO = -101; + state.errCodes.NotFound = -102; + state.errCodes.Misuse = -103; + + // TODO: add any SQLITE_xxx symbols we need here. + state.sq3Codes = Object.create(null); + + const isWorkerErrCode = (n)=>(n<=state.errCodes.Error); + + const opStore = (op,val=-1)=>Atomics.store(state.opBuf, state.opIds[op], val); + const opWait = (op,val=-1)=>Atomics.wait(state.opBuf, state.opIds[op], val); + + const opRun = (op,args)=>{ + opStore(op); + wMsg(op, args); + opWait(op); + return Atomics.load(state.opBuf, state.opIds[op]); + }; + + const wait = (ms,value)=>{ + return new Promise((resolve)=>{ + setTimeout(()=>resolve(value), ms); + }); + }; + + const vfsSyncWrappers = { + xOpen: function f(pFile, name, flags, outFlags = {}){ + if(!f._){ + f._ = { + // TODO: map openFlags to args.fileType names. + }; + } + const args = Object.create(null); + args.fid = pFile; + args.filename = name; + args.sab = new SharedArrayBuffer(state.fileBufferSize); + args.fileType = undefined /*TODO: populate based on SQLITE_OPEN_xxx */; + // TODO: populate args object based on flags: + // args.create, args.readOnly, args.deleteOnClose + args.create = true; + args.deleteOnClose = true; + const rc = opRun('xOpen', args); + if(!rc){ + outFlags.readOnly = args.readOnly; + args.ba = new Uint8Array(args.sab); + state.openFiles[pFile] = args; + } + return rc; + }, + xClose: function(pFile){ + let rc = 0; + if(state.openFiles[pFile]){ + delete state.openFiles[pFile]; + rc = opRun('xClose', pFile); + } + return rc; + } + }; + + + const doSomething = function(){ + //state.ioBuf = new Uint8Array(state.sabIo); + const fid = 37; + let rc = vfsSyncWrappers.xOpen(fid, "/foo/bar/baz.sqlite3",0, {}); + log("open rc =",rc,"state.opBuf[xOpen] =",state.opBuf[state.opIds.xOpen]); + if(isWorkerErrCode(rc)){ + error("open failed with code",rc); + return; + } + log("xSleep()ing before close()ing..."); + opRun('xSleep',{ms: 1500}); + log("wait()ing before close()ing..."); + wait(1500).then(function(){ + rc = vfsSyncWrappers.xClose(fid); + log("xClose rc =",rc,"opBuf =",state.opBuf); + }); + }; + + W.onmessage = function({data}){ + log("Worker.onmessage:",data); + switch(data.type){ + case 'ready': + wMsg('init',state); + state.opBuf = new Int32Array(state.opSab); + state.openFiles = Object.create(null); + doSomething(); + break; + } + }; +}/*doAtomicsStuff*/ + +importScripts('sqlite3.js'); +self.sqlite3InitModule().then((EmscriptenModule)=>doAtomicsStuff(EmscriptenModule.sqlite3)); diff --git a/manifest b/manifest index 709a71cbc9..eeba3142ed 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\skv-vfs\sbranch\sinto\sfiddle-opfs\sbranch.\sAdjust\sspeedtest1\s--size\sflags\sto\saccount\sfor\snew\ssize\slimit. -D 2022-09-16T20:16:50.240 +C Add\sinitial\sbits\sof\san\sexperimental\sasync-impl-via-synchronous-interface\sproxy\sintended\sto\smarshal\sOPFS\svia\ssqlite3_vfs\sAPI. +D 2022-09-17T15:08:22.642 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -484,7 +484,7 @@ F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 F ext/wasm/api/sqlite3-api-oo1.js d7526517f7ad3f6bda16ad66d373bbb71b43168deef7af60eda5c9fe873d1387 -F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 +F ext/wasm/api/sqlite3-api-opfs.js 130f60cc8f5835f9d77d4f12308bf4c8fb6d9c315009fc7239c5d67ff2bc8c67 F ext/wasm/api/sqlite3-api-prologue.js 48ebca4ae340b0242d4f39bbded01bd0588393c8023628be1c454b4db6f7bd6e F ext/wasm/api/sqlite3-api-worker1.js d33062afa045fd4be01ba4abc266801807472558b862b30056211b00c9c347b4 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 @@ -502,7 +502,7 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 -F ext/wasm/index.html 095b9a8cee9aac2654c23686ead22f3452b89d581fb41d34d47b6548546b5365 +F ext/wasm/index.html 8365e47e2aff1829923f0481292948487b27a0755fde9c0d3ad15f7ea0118992 F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f @@ -523,6 +523,7 @@ F ext/wasm/speedtest1.html fbb8e4d1639028443f3687a683be660beca6927920545cf6b1fdf F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 +F ext/wasm/sqlite3-opfs-async-proxy.js c42a097dfbb96abef08554b173a47788f5bc1f58c266f859ba01c1fa3ff8327d F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 @@ -532,6 +533,8 @@ F ext/wasm/testing1.js 7cd8ab255c238b030d928755ae8e91e7d90a12f2ae601b1b8f7827aaa F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c F ext/wasm/wasmfs.make 21a5cf297954a689e0dc2a95299ae158f681cae5e90c10b99d986097815fd42d +F ext/wasm/x-sync-async.html 283539e4fcca8c60fea18dbf1f1c0df168340145a19123f8fd5b70f41291b36f +F ext/wasm/x-sync-async.js 42da502ea0b89bfa226c7ac7555c0c87d4ab8a10221ea6fadb4f7877c26a5137 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2027,8 +2030,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 52d1b185b9f6cee1eb3dec436f47e0f52e4621a127abfad8c27f92fd78147889 ef54961ce69fddb4cfeeff0860288de2858a6f7a5aa396691e8e99933eb9af54 -R e35abbf6d1d9b2b828d30426a9a9f018 +P afb79050e635f3c698e51f06c346cbf23b096cfda7d0f1d8e68514ea0c25b7b7 +R dbdee404fc63f84ef61ddb4fd79c0ad1 U stephan -Z 0ddb259c4784fc46ece386ccc85309e5 +Z 67d0abd2dfaa993776fbe8c511ee53f5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a8bb9e363c..7a184bb350 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -afb79050e635f3c698e51f06c346cbf23b096cfda7d0f1d8e68514ea0c25b7b7 \ No newline at end of file +38da059b472415da52f57de7332fbeb8a91e3add1f4be3ff9c1924b52672f77c \ No newline at end of file From 20a9ed1dc6fd63573e49328d27e955969f18470d Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 17 Sep 2022 18:29:49 +0000 Subject: [PATCH 119/428] Include the kv-vfs as an optional VFS on unix builds if the SQLITE_OS_KV_OPTIONAL compile-time option is present. FossilOrigin-Name: 852812d1e2ec3c53ad7c6c64662b37d861fefcf1baeee3d58eba88bcb3f6d8df --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/os_kv.c | 14 +++++++++++--- src/os_unix.c | 3 +++ src/sqliteInt.h | 4 ++++ 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 82c40c633a..72b6c37ae8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sthe\slatest\strunk\senhancements\sinto\sthe\skv-vfs\sbranch. -D 2022-09-17T17:26:44.762 +C Include\sthe\skv-vfs\sas\san\soptional\sVFS\son\sunix\sbuilds\sif\sthe\nSQLITE_OS_KV_OPTIONAL\scompile-time\soption\sis\spresent. +D 2022-09-17T18:29:49.617 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -574,9 +574,9 @@ F src/notify.c 89a97dc854c3aa62ad5f384ef50c5a4a11d70fcc69f86de3e991573421130ed6 F src/os.c 0eb831ba3575af5277e47f4edd14fdfc90025c67eb25ce5cda634518d308d4e9 F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 -F src/os_kv.c 3a2d319988df9da8f3a4d0c753a1492542bd3017ff7ae5d07814460eac6b0b19 +F src/os_kv.c 96c4062e9ca8f3bb70f43e05b766dcd30fe5c2f125fd70c51ed6ef01474a6593 F src/os_setup.h 0711dbc4678f3ac52d7fe736951b6384a0615387c4ba5135a4764e4e31f4b6a6 -F src/os_unix.c 0fa91925f0b8831fc0156a9c04d39d86f85baf9eef66c98712395e1715cb75cc +F src/os_unix.c 287aa5f5691a2b356780c63e83abaa33549add84227b8313395f04088486d79c F src/os_win.c 8d129ae3e59e0fa900e20d0ad789e96f2e08177f0b00b53cdda65c40331e0902 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 6176d9752eb580419e8fef4592dc417a6b00ddfd43ee22f818819bf8840ceee8 @@ -597,7 +597,7 @@ F src/shell.c.in e7e7c2c69ae86c5ee9e8ad66227203d46ff6dce8700a1b1dababff01c71d33d F src/sqlite.h.in b9b7fd73239d94db20332bb6e504688001e5564b655e1318a4427a1caef4b99e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h a988810c9b21c0dc36dc7a62735012339dc76fc7ab448fb0792721d30eacb69d -F src/sqliteInt.h 44eb45c25d379361ee1bd7fc49205471c6cb8a3727aed42372cf4716f653ebf2 +F src/sqliteInt.h 587be0322bf30f248350e9b0d1d36feeece94f30bcf057686c6425c36b8ed3c5 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -2004,8 +2004,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P ef54961ce69fddb4cfeeff0860288de2858a6f7a5aa396691e8e99933eb9af54 ff46bc9d2a844dde9dafa157114f4d9ee157205ca5c08ed9ee32f018de310e62 -R 4de08b84ca8222edc80a9a00d8c86ac4 +P 37c7ca25a6e837e7f83b96a32e2b1a455730e48a48012c458ae2e734a3ccdd1f +R 32beb89ba30390bdb85cd8d5b10c9a35 U drh -Z f60f4623d1ae77ea9ba72cf01682f06f +Z 6831abb42712ac9e23b5b00f249e84e3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 21c52cde0a..ef09e7e676 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -37c7ca25a6e837e7f83b96a32e2b1a455730e48a48012c458ae2e734a3ccdd1f \ No newline at end of file +852812d1e2ec3c53ad7c6c64662b37d861fefcf1baeee3d58eba88bcb3f6d8df \ No newline at end of file diff --git a/src/os_kv.c b/src/os_kv.c index db95a305b7..c1df1f526d 100644 --- a/src/os_kv.c +++ b/src/os_kv.c @@ -15,7 +15,7 @@ ** text. */ #include -#if SQLITE_OS_KV +#if SQLITE_OS_KV || (SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL)) /***************************************************************************** ** Debugging logic @@ -89,7 +89,7 @@ static int kvvfsSleep(sqlite3_vfs*, int microseconds); static int kvvfsCurrentTime(sqlite3_vfs*, double*); static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); -static sqlite3_vfs kvvfs_vfs = { +static sqlite3_vfs sqlite3OsKvvfsObject = { 1, /* iVersion */ sizeof(KVVfsFile), /* szOsFile */ 1024, /* mxPathname */ @@ -1107,14 +1107,22 @@ static int kvvfsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ *pTimeOut = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; return SQLITE_OK; } +#endif /* SQLITE_OS_KV || SQLITE_OS_UNIX */ +#if SQLITE_OS_KV /* ** This routine is called initialize the KV-vfs as the default VFS. */ int sqlite3_os_init(void){ - return sqlite3_vfs_register(&kvvfs_vfs, 1); + return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 1); } int sqlite3_os_end(void){ return SQLITE_OK; } #endif /* SQLITE_OS_KV */ + +#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) +int sqlite3KvvfsInit(void){ + return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 0); +} +#endif diff --git a/src/os_unix.c b/src/os_unix.c index 784686d0a3..c390b51888 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -8065,6 +8065,9 @@ int sqlite3_os_init(void){ sqlite3_vfs_register(&aVfs[i], i==0); #endif } +#ifdef SQLITE_OS_KV_OPTIONAL + sqlite3KvvfsInit(); +#endif unixBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1); #ifndef SQLITE_OMIT_WAL diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 63e3583c8c..7f1dee6eba 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -5456,4 +5456,8 @@ void sqlite3VectorErrorMsg(Parse*, Expr*); const char **sqlite3CompileOptions(int *pnOpt); #endif +#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) +int sqlite3KvvfsInit(void); +#endif + #endif /* SQLITEINT_H */ From a50d3b7a5de8616d7f8f5a9b64aa78067b4b85a6 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 17 Sep 2022 18:31:31 +0000 Subject: [PATCH 120/428] Fix an uninitialized variable in the decoder kv-vfs. FossilOrigin-Name: 354726aa6c399053785f29104de15091629ce4bc275b9e2205cb3656a9e81cd7 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/os_kv.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 72b6c37ae8..d9677a2ca9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Include\sthe\skv-vfs\sas\san\soptional\sVFS\son\sunix\sbuilds\sif\sthe\nSQLITE_OS_KV_OPTIONAL\scompile-time\soption\sis\spresent. -D 2022-09-17T18:29:49.617 +C Fix\san\suninitialized\svariable\sin\sthe\sdecoder\skv-vfs. +D 2022-09-17T18:31:31.300 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -574,7 +574,7 @@ F src/notify.c 89a97dc854c3aa62ad5f384ef50c5a4a11d70fcc69f86de3e991573421130ed6 F src/os.c 0eb831ba3575af5277e47f4edd14fdfc90025c67eb25ce5cda634518d308d4e9 F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 -F src/os_kv.c 96c4062e9ca8f3bb70f43e05b766dcd30fe5c2f125fd70c51ed6ef01474a6593 +F src/os_kv.c cf02a39ab3271d237890c7de03a49e5ecc19c930b8f4ad6afd43651dc05c9400 F src/os_setup.h 0711dbc4678f3ac52d7fe736951b6384a0615387c4ba5135a4764e4e31f4b6a6 F src/os_unix.c 287aa5f5691a2b356780c63e83abaa33549add84227b8313395f04088486d79c F src/os_win.c 8d129ae3e59e0fa900e20d0ad789e96f2e08177f0b00b53cdda65c40331e0902 @@ -2004,8 +2004,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 37c7ca25a6e837e7f83b96a32e2b1a455730e48a48012c458ae2e734a3ccdd1f -R 32beb89ba30390bdb85cd8d5b10c9a35 +P 852812d1e2ec3c53ad7c6c64662b37d861fefcf1baeee3d58eba88bcb3f6d8df +R 9c6620d4dc4ed0d3ddc9cc9e79cbabf7 U drh -Z 6831abb42712ac9e23b5b00f249e84e3 +Z 7ef728d647d25bb1b8240f0a67ed5ff5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ef09e7e676..1336748789 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -852812d1e2ec3c53ad7c6c64662b37d861fefcf1baeee3d58eba88bcb3f6d8df \ No newline at end of file +354726aa6c399053785f29104de15091629ce4bc275b9e2205cb3656a9e81cd7 \ No newline at end of file diff --git a/src/os_kv.c b/src/os_kv.c index c1df1f526d..fb8eae6270 100644 --- a/src/os_kv.c +++ b/src/os_kv.c @@ -603,7 +603,7 @@ static void kvvfsDecodeJournal( const char *zTxt, /* Text encoding. Zero-terminated */ int nTxt /* Bytes in zTxt, excluding zero terminator */ ){ - unsigned int n; + unsigned int n = 0; int c, i, mult; i = 0; mult = 1; From 0731554629912621874ec7ebd5ab307e270caef4 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 17 Sep 2022 20:50:12 +0000 Subject: [PATCH 121/428] Add the remaining vfs/io_methods wrappers to the OPFS sync/async proxy, but most are not yet tested. FossilOrigin-Name: 44db9132145b3072488ea91db53f6c06be74544beccad5fd07efd22c0f03dc04 --- ext/wasm/sqlite3-opfs-async-proxy.js | 537 ++++++++++++++------------- ext/wasm/x-sync-async.html | 10 +- ext/wasm/x-sync-async.js | 441 ++++++++++++++++++---- manifest | 16 +- manifest.uuid | 2 +- 5 files changed, 671 insertions(+), 335 deletions(-) diff --git a/ext/wasm/sqlite3-opfs-async-proxy.js b/ext/wasm/sqlite3-opfs-async-proxy.js index 98e2688149..88fdfa603a 100644 --- a/ext/wasm/sqlite3-opfs-async-proxy.js +++ b/ext/wasm/sqlite3-opfs-async-proxy.js @@ -20,267 +20,308 @@ https://github.com/rhashimoto/wa-sqlite/blob/master/src/examples/OriginPrivateFileSystemVFS.js for demonstrating how to use the OPFS APIs. + + This file is to be loaded as a Worker. It does not have any direct + access to the sqlite3 JS/WASM bits, so any bits which it needs (most + notably SQLITE_xxx integer codes) have to be imported into it via an + initialization process. */ 'use strict'; -(function(){ - const toss = function(...args){throw new Error(args.join(' '))}; - if(self.window === self){ - toss("This code cannot run from the main thread.", - "Load it as a Worker from a separate Worker."); - }else if(!navigator.storage.getDirectory){ - toss("This API requires navigator.storage.getDirectory."); +const toss = function(...args){throw new Error(args.join(' '))}; +if(self.window === self){ + toss("This code cannot run from the main thread.", + "Load it as a Worker from a separate Worker."); +}else if(!navigator.storage.getDirectory){ + toss("This API requires navigator.storage.getDirectory."); +} +/** + Will hold state copied to this object from the syncronous side of + this API. +*/ +const state = Object.create(null); +/** + verbose: + + 0 = no logging output + 1 = only errors + 2 = warnings and errors + 3 = debug, warnings, and errors +*/ +state.verbose = 2; + +const __logPrefix = "OPFS asyncer:"; +const log = (...args)=>{ + if(state.verbose>2) console.log(__logPrefix,...args); +}; +const warn = (...args)=>{ + if(state.verbose>1) console.warn(__logPrefix,...args); +}; +const error = (...args)=>{ + if(state.verbose) console.error(__logPrefix,...args); +}; + +warn("This file is very much experimental and under construction.",self.location.pathname); + +/** + Map of sqlite3_file pointers (integers) to metadata related to a + given OPFS file handles. The pointers are, in this side of the + interface, opaque file handle IDs provided by the synchronous + part of this constellation. Each value is an object with a structure + demonstrated in the xOpen() impl. +*/ +const __openFiles = Object.create(null); + +/** + Map of dir names to FileSystemDirectoryHandle objects. +*/ +const __dirCache = new Map; + +/** + Takes the absolute path to a filesystem element. Returns an array + of [handleOfContainingDir, filename]. If the 2nd argument is + truthy then each directory element leading to the file is created + along the way. Throws if any creation or resolution fails. +*/ +const getDirForPath = async function f(absFilename, createDirs = false){ + const url = new URL( + absFilename, 'file://xyz' + ) /* use URL to resolve path pieces such as a/../b */; + const path = url.pathname.split('/').filter((v)=>!!v); + const filename = path.pop(); + const allDirs = '/'+path.join('/'); + let dh = __dirCache.get(allDirs); + if(!dh){ + dh = state.rootDir; + for(const dirName of path){ + if(dirName){ + dh = await dh.getDirectoryHandle(dirName, {create: !!createDirs}); + } + } + __dirCache.set(allDirs, dh); } - const logPrefix = "OPFS worker:"; - const log = (...args)=>{ - console.log(logPrefix,...args); - }; - const warn = (...args)=>{ - console.warn(logPrefix,...args); - }; - const error = (...args)=>{ - console.error(logPrefix,...args); - }; + return [dh, filename]; +}; - warn("This file is very much experimental and under construction.",self.location.pathname); - const wMsg = (type,payload)=>postMessage({type,payload}); - const state = Object.create(null); - /*state.opSab; - state.sabIO; - state.opBuf; - state.opIds; - state.rootDir;*/ - /** - Map of sqlite3_file pointers (integers) to metadata related to a - given OPFS file handles. The pointers are, in this side of the - interface, opaque file handle IDs provided by the synchronous - part of this constellation. Each value is an object with a structure - demonstrated in the xOpen() impl. - */ - state.openFiles = Object.create(null); +/** + Stores the given value at the array index reserved for the given op + and then Atomics.notify()'s it. +*/ +const storeAndNotify = (opName, value)=>{ + log(opName+"() is notify()ing w/ value:",value); + Atomics.store(state.opBuf, state.opIds[opName], value); + Atomics.notify(state.opBuf, state.opIds[opName]); +}; - /** - Map of dir names to FileSystemDirectoryHandle objects. - */ - state.dirCache = new Map; +const isInt32 = function(n){ + return ('bigint'!==typeof n /*TypeError: can't convert BigInt to number*/) + && !!(n===(n|0) && n<=2147483647 && n>=-2147483648); +}; +const affirm32Bits = function(n){ + return isInt32(n) || toss("Number is too large (>31 bits) (FIXME!):",n); +}; - const __splitPath = (absFilename)=>{ - const a = absFilename.split('/').filter((v)=>!!v); - return [a, a.pop()]; - }; - /** - Takes the absolute path to a filesystem element. Returns an array - of [handleOfContainingDir, filename]. If the 2nd argument is - truthy then each directory element leading to the file is created - along the way. Throws if any creation or resolution fails. - */ - const getDirForPath = async function f(absFilename, createDirs = false){ - const url = new URL( - absFilename, 'file://xyz' - ) /* use URL to resolve path pieces such as a/../b */; - const [path, filename] = __splitPath(url.pathname); - const allDirs = path.join('/'); - let dh = state.dirCache.get(allDirs); - if(!dh){ - dh = state.rootDir; - for(const dirName of path){ - if(dirName){ - dh = await dh.getDirectoryHandle(dirName, {create: !!createDirs}); - } +/** + Throws if fh is a file-holding object which is flagged as read-only. +*/ +const affirmNotRO = function(opName,fh){ + if(fh.readOnly) toss(opName+"(): File is read-only: "+fh.filenameAbs); +}; + +/** + Asynchronous wrappers for sqlite3_vfs and sqlite3_io_methods + methods. Maintenance reminder: members are in alphabetical order + to simplify finding them. +*/ +const vfsAsyncImpls = { + xAccess: async function({filename, exists, readWrite}){ + warn("xAccess(",arguments[0],") is TODO"); + const rc = state.sq3Codes.SQLITE_IOERR; + storeAndNotify('xAccess', rc); + }, + xClose: async function(fid){ + const opName = 'xClose'; + log(opName+"(",arguments[0],")"); + const fh = __openFiles[fid]; + if(fh){ + delete __openFiles[fid]; + if(fh.accessHandle) await fh.accessHandle.close(); + if(fh.deleteOnClose){ + try{ await fh.dirHandle.removeEntry(fh.filenamePart) } + catch(e){ warn("Ignoring dirHandle.removeEntry() failure of",fh,e) } } - state.dirCache.set(allDirs, dh); + storeAndNotify(opName, 0); + }else{ + storeAndNotify(opName, state.sq3Codes.SQLITE_NOFOUND); } - return [dh, filename]; - }; - - - /** - Generates a random ASCII string len characters long, intended for - use as a temporary file name. - */ - const randomFilename = function f(len=16){ - if(!f._chars){ - f._chars = "abcdefghijklmnopqrstuvwxyz"+ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ - "012346789"; - f._n = f._chars.length; + }, + xDelete: async function({filename, syncDir/*ignored*/}){ + log("xDelete(",arguments[0],")"); + try { + const [hDir, filenamePart] = await getDirForPath(filename, false); + await hDir.removeEntry(filenamePart); + }catch(e){ + /* Ignoring: _presumably_ the file can't be found. */ } - const a = []; - let i = 0; - for( ; i < len; ++i){ - const ndx = Math.random() * (f._n * 64) % f._n | 0; - a[i] = f._chars[ndx]; + storeAndNotify('xDelete', 0); + }, + xFileSize: async function(fid){ + log("xFileSize(",arguments,")"); + const fh = __openFiles[fid]; + let sz; + try{ + sz = await fh.accessHandle.getSize(); + fh.sabViewFileSize.setBigInt64(0, BigInt(sz)); + sz = 0; + }catch(e){ + error("xFileSize():",e, fh); + sz = state.sq3Codes.SQLITE_IOERR; } - return a.join(''); - }; - - const storeAndNotify = (opName, value)=>{ - log(opName+"() is notify()ing w/ value:",value); - Atomics.store(state.opBuf, state.opIds[opName], value); - Atomics.notify(state.opBuf, state.opIds[opName]); - }; - - const isInt32 = function(n){ - return ('bigint'!==typeof n /*TypeError: can't convert BigInt to number*/) - && !!(n===(n|0) && n<=2147483647 && n>=-2147483648); - }; - const affirm32Bits = function(n){ - return isInt32(n) || toss("Number is too large (>31 bits):",n); - }; - - const ioMethods = { - xAccess: async function({filename, exists, readWrite}){ - log("xAccess(",arguments,")"); - const rc = 1; - storeAndNotify('xAccess', rc); - }, - xClose: async function(fid){ - const opName = 'xClose'; + storeAndNotify('xFileSize', sz); + }, + xOpen: async function({ + fid/*sqlite3_file pointer*/, + sab/*file-specific SharedArrayBuffer*/, + filename, + fileType = undefined /*mainDb, mainJournal, etc.*/, + create = false, readOnly = false, deleteOnClose = false + }){ + const opName = 'xOpen'; + try{ + if(create) readOnly = false; log(opName+"(",arguments[0],")"); - log("state.openFiles",state.openFiles); - const fh = state.openFiles[fid]; - if(fh){ - delete state.openFiles[fid]; - //await fh.close(); - if(fh.accessHandle) await fh.accessHandle.close(); - if(fh.deleteOnClose){ - try{ - await fh.dirHandle.removeEntry(fh.filenamePart); - } - catch(e){ - warn("Ignoring dirHandle.removeEntry() failure of",fh); - } - } - log("state.openFiles",state.openFiles); - storeAndNotify(opName, 0); - }else{ - storeAndNotify(opName, state.errCodes.NotFound); - } - }, - xDelete: async function(filename){ - log("xDelete(",arguments,")"); - storeAndNotify('xClose', 0); - }, - xFileSize: async function(fid){ - log("xFileSize(",arguments,")"); - const fh = state.openFiles[fid]; - const sz = await fh.getSize(); - affirm32Bits(sz); - storeAndNotify('xFileSize', sz | 0); - }, - xOpen: async function({ - fid/*sqlite3_file pointer*/, sab/*file-specific SharedArrayBuffer*/, - filename, - fileType = undefined /*mainDb, mainJournal, etc.*/, - create = false, readOnly = false, deleteOnClose = false, - }){ - const opName = 'xOpen'; - try{ - if(create) readOnly = false; - log(opName+"(",arguments[0],")"); - - let hDir, filenamePart, hFile; - try { - [hDir, filenamePart] = await getDirForPath(filename, !!create); - }catch(e){ - storeAndNotify(opName, state.errCodes.NotFound); - return; - } - hFile = await hDir.getFileHandle(filenamePart, {create: !!create}); - log(opName,"filenamePart =",filenamePart, 'hDir =',hDir); - const fobj = state.openFiles[fid] = Object.create(null); - fobj.filenameAbs = filename; - fobj.filenamePart = filenamePart; - fobj.dirHandle = hDir; - fobj.fileHandle = hFile; - fobj.accessHandle = undefined; - fobj.fileType = fileType; - fobj.sab = sab; - fobj.create = !!create; - fobj.readOnly = !!readOnly; - fobj.deleteOnClose = !!deleteOnClose; - - /** - wa-sqlite, at this point, grabs a SyncAccessHandle and - assigns it to the accessHandle prop of the file state - object, but it's unclear why it does that. - */ - storeAndNotify(opName, 0); + let hDir, filenamePart; + try { + [hDir, filenamePart] = await getDirForPath(filename, !!create); }catch(e){ - error(opName,e); - storeAndNotify(opName, state.errCodes.IO); + storeAndNotify(opName, state.sql3Codes.SQLITE_NOTFOUND); + return; } - }, - xRead: async function({fid,n,offset}){ - log("xRead(",arguments,")"); - affirm32Bits(n + offset); - const fh = state.openFiles[fid]; - storeAndNotify('xRead',fid); - }, - xSleep: async function f({ms}){ - log("xSleep(",arguments[0],")"); - await new Promise((resolve)=>{ - setTimeout(()=>resolve(), ms); - }).finally(()=>storeAndNotify('xSleep',0)); - }, - xSync: async function({fid}){ - log("xSync(",arguments,")"); - const fh = state.openFiles[fid]; - await fh.flush(); - storeAndNotify('xSync',fid); - }, - xTruncate: async function({fid,size}){ - log("xTruncate(",arguments,")"); - affirm32Bits(size); - const fh = state.openFiles[fid]; - fh.truncate(size); - storeAndNotify('xTruncate',fid); - }, - xWrite: async function({fid,src,n,offset}){ - log("xWrite(",arguments,")"); - const fh = state.openFiles[fid]; - storeAndNotify('xWrite',fid); + const hFile = await hDir.getFileHandle(filenamePart, {create: !!create}); + log(opName,"filenamePart =",filenamePart, 'hDir =',hDir); + const fobj = __openFiles[fid] = Object.create(null); + fobj.filenameAbs = filename; + fobj.filenamePart = filenamePart; + fobj.dirHandle = hDir; + fobj.fileHandle = hFile; + fobj.fileType = fileType; + fobj.sab = sab; + fobj.sabViewFileSize = new DataView(sab,state.fbInt64Offset,8); + fobj.create = !!create; + fobj.readOnly = !!readOnly; + fobj.deleteOnClose = !!deleteOnClose; + /** + wa-sqlite, at this point, grabs a SyncAccessHandle and + assigns it to the accessHandle prop of the file state + object, but only for certain cases and it's unclear why it + places that limitation on it. + */ + fobj.accessHandle = await hFile.createSyncAccessHandle(); + storeAndNotify(opName, 0); + }catch(e){ + error(opName,e); + storeAndNotify(opName, state.sq3Codes.SQLITE_IOERR); + } + }, + xRead: async function({fid,n,offset}){ + log("xRead(",arguments[0],")"); + let rc = 0; + const fh = __openFiles[fid]; + try{ + const aRead = new Uint8array(fh.sab, n); + const nRead = fh.accessHandle.read(aRead, {at: offset}); + if(nRead < n){/* Zero-fill remaining bytes */ + new Uint8array(fh.sab).fill(0, nRead, n); + rc = state.sq3Codes.SQLITE_IOERR_SHORT_READ; + } + }catch(e){ + error("xRead() failed",e,fh); + rc = state.sq3Codes.SQLITE_IOERR_READ; + } + storeAndNotify('xRead',rc); + }, + xSleep: async function f(ms){ + log("xSleep(",ms,")"); + await new Promise((resolve)=>{ + setTimeout(()=>resolve(), ms); + }).finally(()=>storeAndNotify('xSleep',0)); + }, + xSync: async function({fid,flags/*ignored*/}){ + log("xSync(",arguments[0],")"); + const fh = __openFiles[fid]; + if(!fh.readOnly && fh.accessHandle) await fh.accessHandle.flush(); + storeAndNotify('xSync',0); + }, + xTruncate: async function({fid,size}){ + log("xTruncate(",arguments[0],")"); + let rc = 0; + const fh = __openFiles[fid]; + try{ + affirmNotRO('xTruncate', fh); + await fh.accessHandle.truncate(size); + }catch(e){ + error("xTruncate():",e,fh); + rc = state.sq3Codes.SQLITE_IOERR_TRUNCATE; + } + storeAndNotify('xTruncate',rc); + }, + xWrite: async function({fid,src,n,offset}){ + log("xWrite(",arguments[0],")"); + let rc; + try{ + const fh = __openFiles[fid]; + affirmNotRO('xWrite', fh); + const nOut = fh.accessHandle.write(new UInt8Array(fh.sab, 0, n), {at: offset}); + rc = (nOut===n) ? 0 : state.sq3Codes.SQLITE_IOERR_WRITE; + }catch(e){ + error("xWrite():",e,fh); + rc = state.sq3Codes.SQLITE_IOERR_WRITE; + } + storeAndNotify('xWrite',rc); + } +}; + +navigator.storage.getDirectory().then(function(d){ + const wMsg = (type)=>postMessage({type}); + state.rootDir = d; + log("state.rootDir =",state.rootDir); + self.onmessage = async function({data}){ + log("self.onmessage()",data); + switch(data.type){ + case 'init':{ + /* Receive shared state from synchronous partner */ + const opt = data.payload; + state.verbose = opt.verbose ?? 2; + state.fileBufferSize = opt.fileBufferSize; + state.fbInt64Offset = opt.fbInt64Offset; + state.opSab = opt.opSab; + state.opBuf = new Int32Array(state.opSab); + state.opIds = opt.opIds; + state.sq3Codes = opt.sq3Codes; + Object.keys(vfsAsyncImpls).forEach((k)=>{ + if(!Number.isFinite(state.opIds[k])){ + toss("Maintenance required: missing state.opIds[",k,"]"); + } + }); + log("init state",state); + wMsg('inited'); + break; + } + default:{ + let err; + const m = vfsAsyncImpls[data.type] || toss("Unknown message type:",data.type); + try { + await m(data.payload).catch((e)=>err=e); + }catch(e){ + err = e; + } + if(err){ + error("Error handling",data.type+"():",e); + storeAndNotify(data.type, state.sq3Codes.SQLITE_ERROR); + } + break; + } } }; - - const onReady = function(){ - self.onmessage = async function({data}){ - log("self.onmessage",data); - switch(data.type){ - case 'init':{ - const opt = data.payload; - state.opSab = opt.opSab; - state.opBuf = new Int32Array(state.opSab); - state.opIds = opt.opIds; - state.errCodes = opt.errCodes; - state.sq3Codes = opt.sq3Codes; - Object.keys(ioMethods).forEach((k)=>{ - if(!state.opIds[k]){ - toss("Maintenance required: missing state.opIds[",k,"]"); - } - }); - log("init state",state); - break; - } - default:{ - const m = ioMethods[data.type] || toss("Unknown message type:",data.type); - try { - await m(data.payload); - }catch(e){ - error("Error handling",data.type+"():",e); - storeAndNotify(data.type, -99); - } - break; - } - } - }; - wMsg('ready'); - }; - - navigator.storage.getDirectory().then(function(d){ - state.rootDir = d; - log("state.rootDir =",state.rootDir); - onReady(); - }); - -})(); + wMsg('loaded'); +}); diff --git a/ext/wasm/x-sync-async.html b/ext/wasm/x-sync-async.html index 4b2e08a31f..ec0a6353b5 100644 --- a/ext/wasm/x-sync-async.html +++ b/ext/wasm/x-sync-async.html @@ -13,16 +13,10 @@
    This is an experiment in wrapping the asynchronous OPFS APIs behind a fully synchronous proxy. It is very much incomplete, under construction, and experimental. - See the dev console for all output. + See the dev console for all output.
    - - - + diff --git a/ext/wasm/x-sync-async.js b/ext/wasm/x-sync-async.js index fec7efa73f..c3027585b1 100644 --- a/ext/wasm/x-sync-async.js +++ b/ext/wasm/x-sync-async.js @@ -1,5 +1,29 @@ +/* + 2022-09-17 + + 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 EXTREMELY INCOMPLETE and UNDER CONSTRUCTION experiment for OPFS. + This file holds the synchronous half of an sqlite3_vfs + implementation which proxies, in a synchronous fashion, the + asynchronous OPFS APIs using a second Worker. +*/ 'use strict'; -const doAtomicsStuff = function(sqlite3){ +/** + This function is a placeholder for use in development. When + working, this will be moved into a file named + api/sqlite3-api-opfs.js, or similar, and hooked in to the + sqlite-api build construct. +*/ +const initOpfsVfs = function(sqlite3){ + const toss = function(...args){throw new Error(args.join(' '))}; const logPrefix = "OPFS syncer:"; const log = (...args)=>{ console.log(logPrefix,...args); @@ -10,46 +34,73 @@ const doAtomicsStuff = function(sqlite3){ const error = (...args)=>{ console.error(logPrefix,...args); }; + warn("This file is very much experimental and under construction.",self.location.pathname); + + const capi = sqlite3.capi; + const wasm = capi.wasm; + const sqlite3_vfs = capi.sqlite3_vfs + || toss("Missing sqlite3.capi.sqlite3_vfs object."); + const sqlite3_file = capi.sqlite3_file + || toss("Missing sqlite3.capi.sqlite3_file object."); + const sqlite3_io_methods = capi.sqlite3_io_methods + || toss("Missing sqlite3.capi.sqlite3_io_methods object."); + const StructBinder = sqlite3.StructBinder || toss("Missing sqlite3.StructBinder."); + const W = new Worker("sqlite3-opfs-async-proxy.js"); const wMsg = (type,payload)=>W.postMessage({type,payload}); - warn("This file is very much experimental and under construction.",self.location.pathname); /** State which we send to the async-api Worker or share with it. This object must initially contain only cloneable or sharable - objects. After the worker's "ready" message arrives, other types + objects. After the worker's "inited" message arrives, other types of data may be added to it. */ const state = Object.create(null); + state.verbose = 3; + state.fileBufferSize = 1024 * 64 + 8 /* size of fileHandle.sab. 64k = max sqlite3 page size */; + state.fbInt64Offset = state.fileBufferSize - 8 /*spot in fileHandle.sab to store an int64*/; state.opIds = Object.create(null); - state.opIds.xAccess = 1; - state.opIds.xClose = 2; - state.opIds.xDelete = 3; - state.opIds.xFileSize = 4; - state.opIds.xOpen = 5; - state.opIds.xRead = 6; - state.opIds.xSync = 7; - state.opIds.xTruncate = 8; - state.opIds.xWrite = 9; - state.opIds.xSleep = 10; - state.opIds.xBlock = 99 /* to block worker while this code is still handling something */; - state.opSab = new SharedArrayBuffer(64); - state.fileBufferSize = 1024 * 65 /* 64k = max sqlite3 page size */; - /* TODO: use SQLITE_xxx err codes. */ - state.errCodes = Object.create(null); - state.errCodes.Error = -100; - state.errCodes.IO = -101; - state.errCodes.NotFound = -102; - state.errCodes.Misuse = -103; + { + let i = 0; + state.opIds.xAccess = i++; + state.opIds.xClose = i++; + state.opIds.xDelete = i++; + state.opIds.xFileSize = i++; + state.opIds.xOpen = i++; + state.opIds.xRead = i++; + state.opIds.xSleep = i++; + state.opIds.xSync = i++; + state.opIds.xTruncate = i++; + state.opIds.xWrite = i++; + state.opSab = new SharedArrayBuffer(i * 4); + } - // TODO: add any SQLITE_xxx symbols we need here. state.sq3Codes = Object.create(null); - - const isWorkerErrCode = (n)=>(n<=state.errCodes.Error); + state.sq3Codes._reverse = Object.create(null); + [ // SQLITE_xxx constants to export to the async worker counterpart... + 'SQLITE_ERROR', 'SQLITE_IOERR', + 'SQLITE_NOTFOUND', 'SQLITE_MISUSE', + 'SQLITE_IOERR_READ', 'SQLITE_IOERR_SHORT_READ', + 'SQLITE_IOERR_WRITE', 'SQLITE_IOERR_FSYNC', + 'SQLITE_IOERR_TRUNCATE', 'SQLITE_IOERR_DELETE', + 'SQLITE_IOERR_ACCESS', 'SQLITE_IOERR_CLOSE' + ].forEach(function(k){ + state.sq3Codes[k] = capi[k] || toss("Maintenance required: not found:",k); + state.sq3Codes._reverse[capi[k]] = k; + }); + + const isWorkerErrCode = (n)=>!!state.sq3Codes._reverse[n]; const opStore = (op,val=-1)=>Atomics.store(state.opBuf, state.opIds[op], val); const opWait = (op,val=-1)=>Atomics.wait(state.opBuf, state.opIds[op], val); + /** + Runs the given operation in the async worker counterpart, waits + for its response, and returns the result which the async worker + writes to the given op's index in state.opBuf. The 2nd argument + must be a single object or primitive value, depending on the + given operation's signature in the async API counterpart. + */ const opRun = (op,args)=>{ opStore(op); wMsg(op, args); @@ -63,71 +114,321 @@ const doAtomicsStuff = function(sqlite3){ }); }; - const vfsSyncWrappers = { - xOpen: function f(pFile, name, flags, outFlags = {}){ - if(!f._){ - f._ = { - // TODO: map openFlags to args.fileType names. - }; - } - const args = Object.create(null); - args.fid = pFile; - args.filename = name; - args.sab = new SharedArrayBuffer(state.fileBufferSize); - args.fileType = undefined /*TODO: populate based on SQLITE_OPEN_xxx */; - // TODO: populate args object based on flags: - // args.create, args.readOnly, args.deleteOnClose - args.create = true; - args.deleteOnClose = true; - const rc = opRun('xOpen', args); - if(!rc){ - outFlags.readOnly = args.readOnly; - args.ba = new Uint8Array(args.sab); - state.openFiles[pFile] = args; - } - return rc; + /** + Generates a random ASCII string len characters long, intended for + use as a temporary file name. + */ + const randomFilename = function f(len=16){ + if(!f._chars){ + f._chars = "abcdefghijklmnopqrstuvwxyz"+ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ + "012346789"; + f._n = f._chars.length; + } + const a = []; + let i = 0; + for( ; i < len; ++i){ + const ndx = Math.random() * (f._n * 64) % f._n | 0; + a[i] = f._chars[ndx]; + } + return a.join(''); + }; + + /** + Map of sqlite3_file pointers to objects constructed by xOpen(). + */ + const __openFiles = Object.create(null); + + const pDVfs = capi.sqlite3_vfs_find(null)/*pointer to default VFS*/; + const dVfs = pDVfs + ? new sqlite3_vfs(pDVfs) + : null /* dVfs will be null when sqlite3 is built with + SQLITE_OS_OTHER. Though we cannot currently handle + that case, the hope is to eventually be able to. */; + const opfsVfs = new sqlite3_vfs(); + const opfsIoMethods = new sqlite3_io_methods(); + opfsVfs.$iVersion = 2/*yes, two*/; + opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; + opfsVfs.$mxPathname = 1024/*sure, why not?*/; + opfsVfs.$zName = wasm.allocCString("opfs"); + opfsVfs.ondispose = [ + '$zName', opfsVfs.$zName, + 'cleanup dVfs', ()=>(dVfs ? dVfs.dispose() : null) + ]; + if(dVfs){ + opfsVfs.$xSleep = dVfs.$xSleep; + opfsVfs.$xRandomness = dVfs.$xRandomness; + } + // All C-side memory of opfsVfs is zeroed out, but just to be explicit: + opfsVfs.$xDlOpen = opfsVfs.$xDlError = opfsVfs.$xDlSym = opfsVfs.$xDlClose = null; + /** + Pedantic sidebar about opfsVfs.ondispose: the entries in that array + are items to clean up when opfsVfs.dispose() is called, but in this + environment it will never be called. The VFS instance simply + hangs around until the WASM module instance is cleaned up. We + "could" _hypothetically_ clean it up by "importing" an + sqlite3_os_end() impl into the wasm build, but the shutdown order + of the wasm engine and the JS one are undefined so there is no + guaranty that the opfsVfs instance would be available in one + environment or the other when sqlite3_os_end() is called (_if_ it + gets called at all in a wasm build, which is undefined). + */ + + /** + Impls for the sqlite3_io_methods methods. Maintenance reminder: + members are in alphabetical order to simplify finding them. + */ + const ioSyncWrappers = { + xCheckReservedLock: function(pFile,pOut){ + // Exclusive lock is automatically acquired when opened + //warn("xCheckReservedLock(",arguments,") is a no-op"); + wasm.setMemValue(pOut,1,'i32'); + return 0; }, xClose: function(pFile){ let rc = 0; - if(state.openFiles[pFile]){ - delete state.openFiles[pFile]; + const f = __openFiles[pFile]; + if(f){ + delete __openFiles[pFile]; rc = opRun('xClose', pFile); + if(f.sq3File) f.sq3File.dispose(); } return rc; + }, + xDeviceCharacteristics: function(pFile){ + //debug("xDeviceCharacteristics(",pFile,")"); + return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; + }, + xFileControl: function(pFile,op,pArg){ + //debug("xFileControl(",arguments,") is a no-op"); + return capi.SQLITE_NOTFOUND; + }, + xFileSize: function(pFile,pSz64){ + const rc = opRun('xFileSize', pFile); + if(!isWorkerErrCode(rc)){ + const f = __openFiles[pFile]; + wasm.setMemValue(pSz64, f.sabViewFileSize.getBigInt64(0) ,'i64'); + } + return rc; + }, + xLock: function(pFile,lockType){ + //2022-09: OPFS handles lock when opened + //warn("xLock(",arguments,") is a no-op"); + return 0; + }, + xRead: function(pFile,pDest,n,offset){ + /* int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) */ + const f = __opfsHandles[pFile]; + try { + // FIXME(?): block until we finish copying the xRead result buffer. How? + let rc = opRun('xRead',{fid:pFile, n, offset}); + if(0!==rc) return rc; + let i = 0; + for(; i < n; ++i) wasm.setMemValue(pDest + i, f.sabView[i]); + }catch(e){ + error("xRead(",arguments,") failed:",e,f); + rc = capi.SQLITE_IOERR_READ; + } + return rc; + }, + xSync: function(pFile,flags){ + return opRun('xSync', {fid:pFile, flags}); + }, + xTruncate: function(pFile,sz64){ + return opRun('xTruncate', {fid:pFile, size: sz64}); + }, + xUnlock: function(pFile,lockType){ + //2022-09: OPFS handles lock when opened + //warn("xUnlock(",arguments,") is a no-op"); + return 0; + }, + xWrite: function(pFile,pSrc,n,offset){ + /* int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst) */ + const f = __opfsHandles[pFile]; + try { + let i = 0; + // FIXME(?): block from here until we finish the xWrite. How? + for(; i < n; ++i) f.sabView[i] = wasm.getMemValue(pSrc+i); + return opRun('xWrite',{fid:pFile, n, offset}); + }catch(e){ + error("xWrite(",arguments,") failed:",e,f); + return capi.SQLITE_IOERR_WRITE; + } } - }; + }/*ioSyncWrappers*/; + + /** + Impls for the sqlite3_vfs methods. Maintenance reminder: members + are in alphabetical order to simplify finding them. + */ + const vfsSyncWrappers = { + // TODO: xAccess + xCurrentTime: function(pVfs,pOut){ + /* If it turns out that we need to adjust for timezone, see: + https://stackoverflow.com/a/11760121/1458521 */ + wasm.setMemValue(pOut, 2440587.5 + (new Date().getTime()/86400000), + 'double'); + return 0; + }, + xCurrentTimeInt64: function(pVfs,pOut){ + // TODO: confirm that this calculation is correct + wasm.setMemValue(pOut, (2440587.5 * 86400000) + new Date().getTime(), + 'i64'); + return 0; + }, + xDelete: function(pVfs, zName, doSyncDir){ + return opRun('xDelete', {filename: wasm.cstringToJs(zName), syncDir: doSyncDir}); + }, + xFullPathname: function(pVfs,zName,nOut,pOut){ + /* Until/unless we have some notion of "current dir" + in OPFS, simply copy zName to pOut... */ + const i = wasm.cstrncpy(pOut, zName, nOut); + return ipMethods is NULL. */ + if(args.readOnly){ + wasm.setMemValue(pOutFlags, capi.SQLITE_OPEN_READONLY, 'i32'); + } + __openFiles[pFile] = args; + args.sabView = new Uint8Array(args.sab); + args.sabViewFileSize = new DataView(args.sab, state.fbInt64Offset, 8); + args.sq3File = new sqlite3_file(pFile); + args.sq3File.$pMethods = opfsIoMethods.pointer; + args.ba = new Uint8Array(args.sab); + } + return rc; + }/*xOpen()*/ + }/*vfsSyncWrappers*/; + if(!opfsVfs.$xRandomness){ + /* If the default VFS has no xRandomness(), add a basic JS impl... */ + vfsSyncWrappers.xRandomness = function(pVfs, nOut, pOut){ + const heap = wasm.heap8u(); + let i = 0; + for(; i < nOut; ++i) heap[pOut + i] = (Math.random()*255000) & 0xFF; + return i; + }; + } + if(!opfsVfs.$xSleep){ + /* If we can inherit an xSleep() impl from the default VFS then + use it, otherwise install one which is certainly less accurate + because it has to go round-trip through the async worker, but + provides the only option for a synchronous sleep() in JS. */ + vfsSyncWrappers.xSleep = (pVfs,ms)=>opRun('xSleep',ms); + } - const doSomething = function(){ + /* + TODO: plug in the above functions in to opfsVfs and opfsIoMethods. + Code for doing so is in api/sqlite3-api-opfs.js. + */ + + const sanityCheck = async function(){ //state.ioBuf = new Uint8Array(state.sabIo); - const fid = 37; - let rc = vfsSyncWrappers.xOpen(fid, "/foo/bar/baz.sqlite3",0, {}); - log("open rc =",rc,"state.opBuf[xOpen] =",state.opBuf[state.opIds.xOpen]); - if(isWorkerErrCode(rc)){ - error("open failed with code",rc); - return; - } - log("xSleep()ing before close()ing..."); - opRun('xSleep',{ms: 1500}); - log("wait()ing before close()ing..."); - wait(1500).then(function(){ - rc = vfsSyncWrappers.xClose(fid); + const scope = wasm.scopedAllocPush(); + const sq3File = new sqlite3_file(); + try{ + const fid = sq3File.pointer; + const openFlags = capi.SQLITE_OPEN_CREATE + | capi.SQLITE_OPEN_READWRITE + | capi.SQLITE_OPEN_DELETEONCLOSE + | capi.SQLITE_OPEN_MAIN_DB; + const pOut = wasm.scopedAlloc(8); + const dbFile = "/sanity/check/file"; + let rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, dbFile, + fid, openFlags, pOut); + log("open rc =",rc,"state.opBuf[xOpen] =",state.opBuf[state.opIds.xOpen]); + if(isWorkerErrCode(rc)){ + error("open failed with code",rc); + return; + } + rc = ioSyncWrappers.xSync(sq3File.pointer, 0); + if(rc) toss('sync failed w/ rc',rc); + rc = ioSyncWrappers.xTruncate(sq3File.pointer, 1024); + if(rc) toss('truncate failed w/ rc',rc); + wasm.setMemValue(pOut,0,'i64'); + rc = ioSyncWrappers.xFileSize(sq3File.pointer, pOut); + if(rc) toss('xFileSize failed w/ rc',rc); + log("xFileSize says:",wasm.getMemValue(pOut, 'i64')); + log("xSleep()ing before close()ing..."); + opRun('xSleep',1500); + rc = ioSyncWrappers.xClose(fid); log("xClose rc =",rc,"opBuf =",state.opBuf); - }); + log("Deleting file:",dbFile); + opRun('xDelete', dbFile); + }finally{ + sq3File.dispose(); + wasm.scopedAllocPop(scope); + } }; + W.onmessage = function({data}){ log("Worker.onmessage:",data); switch(data.type){ - case 'ready': + case 'loaded': + /*Pass our config and shared state on to the async worker.*/ wMsg('init',state); + break; + case 'inited': + /*Indicates that the async partner has received the 'init', + so we now know that the state object is no longer subject to + being copied by a pending postMessage() call.*/ state.opBuf = new Int32Array(state.opSab); - state.openFiles = Object.create(null); - doSomething(); + sanityCheck(); + break; + default: + error("Unexpected message from the async worker:",data); break; } }; -}/*doAtomicsStuff*/ +}/*initOpfsVfs*/ importScripts('sqlite3.js'); -self.sqlite3InitModule().then((EmscriptenModule)=>doAtomicsStuff(EmscriptenModule.sqlite3)); +self.sqlite3InitModule().then((EmscriptenModule)=>initOpfsVfs(EmscriptenModule.sqlite3)); diff --git a/manifest b/manifest index eeba3142ed..cce6558812 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sinitial\sbits\sof\san\sexperimental\sasync-impl-via-synchronous-interface\sproxy\sintended\sto\smarshal\sOPFS\svia\ssqlite3_vfs\sAPI. -D 2022-09-17T15:08:22.642 +C Add\sthe\sremaining\svfs/io_methods\swrappers\sto\sthe\sOPFS\ssync/async\sproxy,\sbut\smost\sare\snot\syet\stested. +D 2022-09-17T20:50:12.684 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -523,7 +523,7 @@ F ext/wasm/speedtest1.html fbb8e4d1639028443f3687a683be660beca6927920545cf6b1fdf F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 -F ext/wasm/sqlite3-opfs-async-proxy.js c42a097dfbb96abef08554b173a47788f5bc1f58c266f859ba01c1fa3ff8327d +F ext/wasm/sqlite3-opfs-async-proxy.js 62024877ad13fdff1834581ca1951ab58bda431e4d548aaaf4506ea54f0ed2de F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 @@ -533,8 +533,8 @@ F ext/wasm/testing1.js 7cd8ab255c238b030d928755ae8e91e7d90a12f2ae601b1b8f7827aaa F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c F ext/wasm/wasmfs.make 21a5cf297954a689e0dc2a95299ae158f681cae5e90c10b99d986097815fd42d -F ext/wasm/x-sync-async.html 283539e4fcca8c60fea18dbf1f1c0df168340145a19123f8fd5b70f41291b36f -F ext/wasm/x-sync-async.js 42da502ea0b89bfa226c7ac7555c0c87d4ab8a10221ea6fadb4f7877c26a5137 +F ext/wasm/x-sync-async.html 717b0d3bee96e49cbd36731bead497ab27a8bf3a3b23dd11e40e61d4ac9e8b80 +F ext/wasm/x-sync-async.js 05c0b49adae0600c5ad12f3325e0873ab1f07b99c2bb017f32b50a4f701490f1 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2030,8 +2030,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P afb79050e635f3c698e51f06c346cbf23b096cfda7d0f1d8e68514ea0c25b7b7 -R dbdee404fc63f84ef61ddb4fd79c0ad1 +P 38da059b472415da52f57de7332fbeb8a91e3add1f4be3ff9c1924b52672f77c +R 8887512b2075b067b4aa7969d08f1316 U stephan -Z 67d0abd2dfaa993776fbe8c511ee53f5 +Z 1bf4c438e48bd1dc1717362060b2f357 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7a184bb350..7d2261af8e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -38da059b472415da52f57de7332fbeb8a91e3add1f4be3ff9c1924b52672f77c \ No newline at end of file +44db9132145b3072488ea91db53f6c06be74544beccad5fd07efd22c0f03dc04 \ No newline at end of file From e6f8a095325ed6b183aacafa8bd2b9cd7f69ae76 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 17 Sep 2022 21:13:26 +0000 Subject: [PATCH 122/428] Generic cleanups in the OPFS sync/async proxy. FossilOrigin-Name: f36bddbe54c3acbfaa958042e4d24724f130bdca551401033f9bc63f3da73492 --- ext/wasm/sqlite3-opfs-async-proxy.js | 16 +++---- ext/wasm/x-sync-async.js | 70 +++++++++++++++++++++------- manifest | 14 +++--- manifest.uuid | 2 +- 4 files changed, 70 insertions(+), 32 deletions(-) diff --git a/ext/wasm/sqlite3-opfs-async-proxy.js b/ext/wasm/sqlite3-opfs-async-proxy.js index 88fdfa603a..1ff9397027 100644 --- a/ext/wasm/sqlite3-opfs-async-proxy.js +++ b/ext/wasm/sqlite3-opfs-async-proxy.js @@ -10,10 +10,10 @@ *********************************************************************** - A EXTREMELY INCOMPLETE and UNDER CONSTRUCTION experiment for OPFS: a - Worker which manages asynchronous OPFS handles on behalf of a - synchronous API which controls it via a combination of Worker - messages, SharedArrayBuffer, and Atomics. + An INCOMPLETE and UNDER CONSTRUCTION experiment for OPFS: a Worker + which manages asynchronous OPFS handles on behalf of a synchronous + API which controls it via a combination of Worker messages, + SharedArrayBuffer, and Atomics. Highly indebted to: @@ -109,8 +109,8 @@ const getDirForPath = async function f(absFilename, createDirs = false){ */ const storeAndNotify = (opName, value)=>{ log(opName+"() is notify()ing w/ value:",value); - Atomics.store(state.opBuf, state.opIds[opName], value); - Atomics.notify(state.opBuf, state.opIds[opName]); + Atomics.store(state.opSABView, state.opIds[opName], value); + Atomics.notify(state.opSABView, state.opIds[opName]); }; const isInt32 = function(n){ @@ -294,8 +294,8 @@ navigator.storage.getDirectory().then(function(d){ state.verbose = opt.verbose ?? 2; state.fileBufferSize = opt.fileBufferSize; state.fbInt64Offset = opt.fbInt64Offset; - state.opSab = opt.opSab; - state.opBuf = new Int32Array(state.opSab); + state.opSAB = opt.opSAB; + state.opSABView = new Int32Array(state.opSAB); state.opIds = opt.opIds; state.sq3Codes = opt.sq3Codes; Object.keys(vfsAsyncImpls).forEach((k)=>{ diff --git a/ext/wasm/x-sync-async.js b/ext/wasm/x-sync-async.js index c3027585b1..9329e765ac 100644 --- a/ext/wasm/x-sync-async.js +++ b/ext/wasm/x-sync-async.js @@ -10,10 +10,20 @@ *********************************************************************** - A EXTREMELY INCOMPLETE and UNDER CONSTRUCTION experiment for OPFS. + An INCOMPLETE and UNDER CONSTRUCTION experiment for OPFS. This file holds the synchronous half of an sqlite3_vfs implementation which proxies, in a synchronous fashion, the - asynchronous OPFS APIs using a second Worker. + asynchronous OPFS APIs using a second Worker, implemented + in sqlite3-opfs-async-proxy.js. + + Summary of how this works: + + This file uses the sqlite3.StructBinder-created struct wrappers for + sqlite3_vfs, sqlite3_io_methods, ans sqlite3_file to set up a + conventional sqlite3_vfs (except that it's implemented in JS). The + methods which require OPFS APIs use a separate worker (hereafter called the + OPFS worker) to access that functionality. This worker and that one + use SharedBufferArray */ 'use strict'; /** @@ -36,6 +46,16 @@ const initOpfsVfs = function(sqlite3){ }; warn("This file is very much experimental and under construction.",self.location.pathname); + if(self.window===self || + !self.SharedBufferArray || + !self.FileSystemHandle || + !self.FileSystemDirectoryHandle || + !self.FileSystemFileHandle || + !self.FileSystemFileHandle.prototype.createSyncAccessHandle || + !navigator.storage.getDirectory){ + warn("This environment does not have OPFS support."); + } + const capi = sqlite3.capi; const wasm = capi.wasm; const sqlite3_vfs = capi.sqlite3_vfs @@ -44,7 +64,6 @@ const initOpfsVfs = function(sqlite3){ || toss("Missing sqlite3.capi.sqlite3_file object."); const sqlite3_io_methods = capi.sqlite3_io_methods || toss("Missing sqlite3.capi.sqlite3_io_methods object."); - const StructBinder = sqlite3.StructBinder || toss("Missing sqlite3.StructBinder."); const W = new Worker("sqlite3-opfs-async-proxy.js"); const wMsg = (type,payload)=>W.postMessage({type,payload}); @@ -72,7 +91,7 @@ const initOpfsVfs = function(sqlite3){ state.opIds.xSync = i++; state.opIds.xTruncate = i++; state.opIds.xWrite = i++; - state.opSab = new SharedArrayBuffer(i * 4); + state.opSAB = new SharedArrayBuffer(i * 4/*sizeof int32*/); } state.sq3Codes = Object.create(null); @@ -91,13 +110,13 @@ const initOpfsVfs = function(sqlite3){ const isWorkerErrCode = (n)=>!!state.sq3Codes._reverse[n]; - const opStore = (op,val=-1)=>Atomics.store(state.opBuf, state.opIds[op], val); - const opWait = (op,val=-1)=>Atomics.wait(state.opBuf, state.opIds[op], val); + const opStore = (op,val=-1)=>Atomics.store(state.opSABView, state.opIds[op], val); + const opWait = (op,val=-1)=>Atomics.wait(state.opSABView, state.opIds[op], val); /** Runs the given operation in the async worker counterpart, waits for its response, and returns the result which the async worker - writes to the given op's index in state.opBuf. The 2nd argument + writes to the given op's index in state.opSABView. The 2nd argument must be a single object or primitive value, depending on the given operation's signature in the async API counterpart. */ @@ -105,7 +124,7 @@ const initOpfsVfs = function(sqlite3){ opStore(op); wMsg(op, args); opWait(op); - return Atomics.load(state.opBuf, state.opIds[op]); + return Atomics.load(state.opSABView, state.opIds[op]); }; const wait = (ms,value)=>{ @@ -151,16 +170,17 @@ const initOpfsVfs = function(sqlite3){ opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; opfsVfs.$mxPathname = 1024/*sure, why not?*/; opfsVfs.$zName = wasm.allocCString("opfs"); + // All C-side memory of opfsVfs is zeroed out, but just to be explicit: + opfsVfs.$xDlOpen = opfsVfs.$xDlError = opfsVfs.$xDlSym = opfsVfs.$xDlClose = null; opfsVfs.ondispose = [ '$zName', opfsVfs.$zName, - 'cleanup dVfs', ()=>(dVfs ? dVfs.dispose() : null) + 'cleanup default VFS wrapper', ()=>(dVfs ? dVfs.dispose() : null), + 'cleanup opfsIoMethods', ()=>opfsIoMethods.dispose() ]; if(dVfs){ opfsVfs.$xSleep = dVfs.$xSleep; opfsVfs.$xRandomness = dVfs.$xRandomness; } - // All C-side memory of opfsVfs is zeroed out, but just to be explicit: - opfsVfs.$xDlOpen = opfsVfs.$xDlError = opfsVfs.$xDlSym = opfsVfs.$xDlClose = null; /** Pedantic sidebar about opfsVfs.ondispose: the entries in that array are items to clean up when opfsVfs.dispose() is called, but in this @@ -383,7 +403,7 @@ const initOpfsVfs = function(sqlite3){ const dbFile = "/sanity/check/file"; let rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, dbFile, fid, openFlags, pOut); - log("open rc =",rc,"state.opBuf[xOpen] =",state.opBuf[state.opIds.xOpen]); + log("open rc =",rc,"state.opSABView[xOpen] =",state.opSABView[state.opIds.xOpen]); if(isWorkerErrCode(rc)){ error("open failed with code",rc); return; @@ -399,7 +419,7 @@ const initOpfsVfs = function(sqlite3){ log("xSleep()ing before close()ing..."); opRun('xSleep',1500); rc = ioSyncWrappers.xClose(fid); - log("xClose rc =",rc,"opBuf =",state.opBuf); + log("xClose rc =",rc,"opSABView =",state.opSABView); log("Deleting file:",dbFile); opRun('xDelete', dbFile); }finally{ @@ -416,13 +436,31 @@ const initOpfsVfs = function(sqlite3){ /*Pass our config and shared state on to the async worker.*/ wMsg('init',state); break; - case 'inited': + case 'inited':{ /*Indicates that the async partner has received the 'init', so we now know that the state object is no longer subject to being copied by a pending postMessage() call.*/ - state.opBuf = new Int32Array(state.opSab); - sanityCheck(); + try { + const rc = capi.sqlite3_vfs_register(opfsVfs.pointer, opfsVfs.$zName); + if(rc){ + opfsVfs.dispose(); + toss("sqlite3_vfs_register(OPFS) failed with rc",rc); + } + if(opfsVfs.pointer !== capi.sqlite3_vfs_find("opfs")){ + toss("BUG: sqlite3_vfs_find() failed for just-installed OPFS VFS"); + } + capi.sqlite3_vfs_register.addReference(opfsVfs, opfsIoMethods); + state.opSABView = new Int32Array(state.opSAB); + if(self.location && +self.location.port > 1024){ + log("Running sanity check for dev mode..."); + sanityCheck(); + } + warn("End of (very incomplete) OPFS setup.", opfsVfs); + }catch(e){ + error(e); + } break; + } default: error("Unexpected message from the async worker:",data); break; diff --git a/manifest b/manifest index cce6558812..bef8fab86f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\sremaining\svfs/io_methods\swrappers\sto\sthe\sOPFS\ssync/async\sproxy,\sbut\smost\sare\snot\syet\stested. -D 2022-09-17T20:50:12.684 +C Generic\scleanups\sin\sthe\sOPFS\ssync/async\sproxy. +D 2022-09-17T21:13:26.228 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -523,7 +523,7 @@ F ext/wasm/speedtest1.html fbb8e4d1639028443f3687a683be660beca6927920545cf6b1fdf F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 -F ext/wasm/sqlite3-opfs-async-proxy.js 62024877ad13fdff1834581ca1951ab58bda431e4d548aaaf4506ea54f0ed2de +F ext/wasm/sqlite3-opfs-async-proxy.js ceb5e3a190bfd42d25fc137b3aaf09adf6469b5f1e33528a64ce7ff74e5ef5b1 F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 @@ -534,7 +534,7 @@ F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c2 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c F ext/wasm/wasmfs.make 21a5cf297954a689e0dc2a95299ae158f681cae5e90c10b99d986097815fd42d F ext/wasm/x-sync-async.html 717b0d3bee96e49cbd36731bead497ab27a8bf3a3b23dd11e40e61d4ac9e8b80 -F ext/wasm/x-sync-async.js 05c0b49adae0600c5ad12f3325e0873ab1f07b99c2bb017f32b50a4f701490f1 +F ext/wasm/x-sync-async.js c99a6f98a8b0ccb43b2883a1bc1da42d0d84b26b5d4fd99d2f053ffe209a0a1f F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2030,8 +2030,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 38da059b472415da52f57de7332fbeb8a91e3add1f4be3ff9c1924b52672f77c -R 8887512b2075b067b4aa7969d08f1316 +P 44db9132145b3072488ea91db53f6c06be74544beccad5fd07efd22c0f03dc04 +R 75c9aa6717389505aa78f157abdf6d44 U stephan -Z 1bf4c438e48bd1dc1717362060b2f357 +Z 6a0a0bcb470dbb44e9d0c87c09f1fedd # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7d2261af8e..4f626ec440 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -44db9132145b3072488ea91db53f6c06be74544beccad5fd07efd22c0f03dc04 \ No newline at end of file +f36bddbe54c3acbfaa958042e4d24724f130bdca551401033f9bc63f3da73492 \ No newline at end of file From 8200a6d8d7685ab252667768a4c291f1f0e9993c Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 17 Sep 2022 23:29:27 +0000 Subject: [PATCH 123/428] Implement OPFS xAccess(), albeit with more limited semantics than the VFS API calls for. Add a way for OPFS xDelete() to optionally recursively remove empty dirs left over after deleting a file. FossilOrigin-Name: c342b5d745f301104c59851c753287ebbbe95a69a56cb522d376d0f3e352c30f --- ext/wasm/sqlite3-opfs-async-proxy.js | 93 ++++++++++++++++++---------- ext/wasm/x-sync-async.js | 30 ++++++--- manifest | 14 ++--- manifest.uuid | 2 +- 4 files changed, 93 insertions(+), 46 deletions(-) diff --git a/ext/wasm/sqlite3-opfs-async-proxy.js b/ext/wasm/sqlite3-opfs-async-proxy.js index 1ff9397027..4a60e8aa1a 100644 --- a/ext/wasm/sqlite3-opfs-async-proxy.js +++ b/ext/wasm/sqlite3-opfs-async-proxy.js @@ -72,9 +72,17 @@ warn("This file is very much experimental and under construction.",self.location const __openFiles = Object.create(null); /** - Map of dir names to FileSystemDirectoryHandle objects. + Expects an OPFS file path. It gets resolved, such that ".." + components are properly expanded, and returned. If the 2nd + are is true, it's returned as an array of path elements, + else it's returned as an absolute path string. */ -const __dirCache = new Map; +const getResolvedPath = function(filename,splitIt){ + const p = new URL( + filename, 'file://irrelevant' + ).pathname; + return splitIt ? p.split('/').filter((v)=>!!v) : p; +} /** Takes the absolute path to a filesystem element. Returns an array @@ -83,21 +91,13 @@ const __dirCache = new Map; along the way. Throws if any creation or resolution fails. */ const getDirForPath = async function f(absFilename, createDirs = false){ - const url = new URL( - absFilename, 'file://xyz' - ) /* use URL to resolve path pieces such as a/../b */; - const path = url.pathname.split('/').filter((v)=>!!v); + const path = getResolvedPath(absFilename, true); const filename = path.pop(); - const allDirs = '/'+path.join('/'); - let dh = __dirCache.get(allDirs); - if(!dh){ - dh = state.rootDir; - for(const dirName of path){ - if(dirName){ - dh = await dh.getDirectoryHandle(dirName, {create: !!createDirs}); - } + let dh = state.rootDir; + for(const dirName of path){ + if(dirName){ + dh = await dh.getDirectoryHandle(dirName, {create: !!createDirs}); } - __dirCache.set(allDirs, dh); } return [dh, filename]; }; @@ -113,14 +113,6 @@ const storeAndNotify = (opName, value)=>{ Atomics.notify(state.opSABView, state.opIds[opName]); }; -const isInt32 = function(n){ - return ('bigint'!==typeof n /*TypeError: can't convert BigInt to number*/) - && !!(n===(n|0) && n<=2147483647 && n>=-2147483648); -}; -const affirm32Bits = function(n){ - return isInt32(n) || toss("Number is too large (>31 bits) (FIXME!):",n); -}; - /** Throws if fh is a file-holding object which is flagged as read-only. */ @@ -134,9 +126,26 @@ const affirmNotRO = function(opName,fh){ to simplify finding them. */ const vfsAsyncImpls = { - xAccess: async function({filename, exists, readWrite}){ - warn("xAccess(",arguments[0],") is TODO"); - const rc = state.sq3Codes.SQLITE_IOERR; + xAccess: async function(filename){ + log("xAccess(",arguments[0],")"); + /* OPFS cannot support the full range of xAccess() queries sqlite3 + calls for. We can essentially just tell if the file is + accessible, but if it is it's automatically writable (unless + it's locked, which we cannot(?) know without trying to open + it). OPFS does not have the notion of read-only. + + The return semantics of this function differ from sqlite3's + xAccess semantics because we are limited in what we can + communicate back to our synchronous communication partner: 0 = + accessible, non-0 means not accessible. + */ + let rc = 0; + try{ + const [dh, fn] = await getDirForPath(filename); + await dh.getFileHandle(fn); + }catch(e){ + rc = state.sq3Codes.SQLITE_IOERR; + } storeAndNotify('xAccess', rc); }, xClose: async function(fid){ @@ -156,12 +165,34 @@ const vfsAsyncImpls = { } }, xDelete: async function({filename, syncDir/*ignored*/}){ + /* The syncDir flag is, for purposes of the VFS API's semantics, + ignored here. However, if it has the value 0x1234 then: after + deleting the given file, recursively try to delete any empty + directories left behind in its wake (ignoring any errors and + stopping at the first failure). + + That said: we don't know for sure that removeEntry() fails if + the dir is not empty because the API is not documented. It has, + however, a "recursive" flag which defaults to false, so + presumably it will fail if the dir is not empty and that flag + is false. + */ log("xDelete(",arguments[0],")"); try { - const [hDir, filenamePart] = await getDirForPath(filename, false); - await hDir.removeEntry(filenamePart); + while(filename){ + const [hDir, filenamePart] = await getDirForPath(filename, false); + //log("Removing:",hDir, filenamePart); + if(!filenamePart) break; + await hDir.removeEntry(filenamePart); + if(0x1234 !== syncDir) break; + filename = getResolvedPath(filename, true); + filename.pop(); + filename = filename.join('/'); + } }catch(e){ - /* Ignoring: _presumably_ the file can't be found. */ + /* Ignoring: _presumably_ the file can't be found or a dir is + not empty. */ + //error("Delete failed",filename, e.message); } storeAndNotify('xDelete', 0); }, @@ -268,8 +299,8 @@ const vfsAsyncImpls = { xWrite: async function({fid,src,n,offset}){ log("xWrite(",arguments[0],")"); let rc; + const fh = __openFiles[fid]; try{ - const fh = __openFiles[fid]; affirmNotRO('xWrite', fh); const nOut = fh.accessHandle.write(new UInt8Array(fh.sab, 0, n), {at: offset}); rc = (nOut===n) ? 0 : state.sq3Codes.SQLITE_IOERR_WRITE; @@ -324,4 +355,4 @@ navigator.storage.getDirectory().then(function(d){ } }; wMsg('loaded'); -}); +}).catch((e)=>error(e)); diff --git a/ext/wasm/x-sync-async.js b/ext/wasm/x-sync-async.js index 9329e765ac..250fde349f 100644 --- a/ext/wasm/x-sync-async.js +++ b/ext/wasm/x-sync-async.js @@ -47,13 +47,14 @@ const initOpfsVfs = function(sqlite3){ warn("This file is very much experimental and under construction.",self.location.pathname); if(self.window===self || - !self.SharedBufferArray || + !self.SharedArrayBuffer || !self.FileSystemHandle || !self.FileSystemDirectoryHandle || !self.FileSystemFileHandle || !self.FileSystemFileHandle.prototype.createSyncAccessHandle || !navigator.storage.getDirectory){ warn("This environment does not have OPFS support."); + return; } const capi = sqlite3.capi; @@ -282,7 +283,11 @@ const initOpfsVfs = function(sqlite3){ are in alphabetical order to simplify finding them. */ const vfsSyncWrappers = { - // TODO: xAccess + xAccess: function(pVfs,zName,flags,pOut){ + const rc = opRun('xAccess', wasm.cstringToJs(zName)); + wasm.setMemValue(pOut, rc ? 0 : 1, 'i32'); + return 0; + }, xCurrentTime: function(pVfs,pOut){ /* If it turns out that we need to adjust for timezone, see: https://stackoverflow.com/a/11760121/1458521 */ @@ -397,17 +402,25 @@ const initOpfsVfs = function(sqlite3){ const fid = sq3File.pointer; const openFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE - | capi.SQLITE_OPEN_DELETEONCLOSE + //| capi.SQLITE_OPEN_DELETEONCLOSE | capi.SQLITE_OPEN_MAIN_DB; const pOut = wasm.scopedAlloc(8); const dbFile = "/sanity/check/file"; - let rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, dbFile, - fid, openFlags, pOut); + const zDbFile = wasm.scopedAllocCString(dbFile); + let rc; + vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.getMemValue(pOut,'i32'); + log("xAccess(",dbFile,") exists ?=",rc); + rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile, + fid, openFlags, pOut); log("open rc =",rc,"state.opSABView[xOpen] =",state.opSABView[state.opIds.xOpen]); if(isWorkerErrCode(rc)){ error("open failed with code",rc); return; } + vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.getMemValue(pOut,'i32'); + if(!rc) toss("xAccess() failed to detect file."); rc = ioSyncWrappers.xSync(sq3File.pointer, 0); if(rc) toss('sync failed w/ rc',rc); rc = ioSyncWrappers.xTruncate(sq3File.pointer, 1024); @@ -417,11 +430,14 @@ const initOpfsVfs = function(sqlite3){ if(rc) toss('xFileSize failed w/ rc',rc); log("xFileSize says:",wasm.getMemValue(pOut, 'i64')); log("xSleep()ing before close()ing..."); - opRun('xSleep',1500); + opRun('xSleep',1000); rc = ioSyncWrappers.xClose(fid); log("xClose rc =",rc,"opSABView =",state.opSABView); log("Deleting file:",dbFile); - opRun('xDelete', dbFile); + vfsSyncWrappers.xDelete(opfsVfs.pointer, zDbFile, 0x1234); + vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.getMemValue(pOut,'i32'); + if(rc) toss("Expecting 0 from xAccess(",dbFile,") after xDelete()."); }finally{ sq3File.dispose(); wasm.scopedAllocPop(scope); diff --git a/manifest b/manifest index bef8fab86f..503b895473 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Generic\scleanups\sin\sthe\sOPFS\ssync/async\sproxy. -D 2022-09-17T21:13:26.228 +C Implement\sOPFS\sxAccess(),\salbeit\swith\smore\slimited\ssemantics\sthan\sthe\sVFS\sAPI\scalls\sfor.\sAdd\sa\sway\sfor\sOPFS\sxDelete()\sto\soptionally\srecursively\sremove\sempty\sdirs\sleft\sover\safter\sdeleting\sa\sfile. +D 2022-09-17T23:29:27.832 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -523,7 +523,7 @@ F ext/wasm/speedtest1.html fbb8e4d1639028443f3687a683be660beca6927920545cf6b1fdf F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 -F ext/wasm/sqlite3-opfs-async-proxy.js ceb5e3a190bfd42d25fc137b3aaf09adf6469b5f1e33528a64ce7ff74e5ef5b1 +F ext/wasm/sqlite3-opfs-async-proxy.js c4be9e614abea5f237564757141e3e128f0cff4b4cd8350f68e17347230fd34e F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 @@ -534,7 +534,7 @@ F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c2 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c F ext/wasm/wasmfs.make 21a5cf297954a689e0dc2a95299ae158f681cae5e90c10b99d986097815fd42d F ext/wasm/x-sync-async.html 717b0d3bee96e49cbd36731bead497ab27a8bf3a3b23dd11e40e61d4ac9e8b80 -F ext/wasm/x-sync-async.js c99a6f98a8b0ccb43b2883a1bc1da42d0d84b26b5d4fd99d2f053ffe209a0a1f +F ext/wasm/x-sync-async.js a95e8acb04fd69526317ee63bf25fa478a20e223d5dc16f7526baf5d81e07d1e F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2030,8 +2030,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 44db9132145b3072488ea91db53f6c06be74544beccad5fd07efd22c0f03dc04 -R 75c9aa6717389505aa78f157abdf6d44 +P f36bddbe54c3acbfaa958042e4d24724f130bdca551401033f9bc63f3da73492 +R 5aad77d6a3a0d677e9c84e63e20c4294 U stephan -Z 6a0a0bcb470dbb44e9d0c87c09f1fedd +Z 31fefe04f3f471aad99d738829acf9a8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4f626ec440..6035cd7f00 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f36bddbe54c3acbfaa958042e4d24724f130bdca551401033f9bc63f3da73492 \ No newline at end of file +c342b5d745f301104c59851c753287ebbbe95a69a56cb522d376d0f3e352c30f \ No newline at end of file From 5f4ad926817650b919ef1a9a987b8b70f906e9d9 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 17 Sep 2022 23:47:20 +0000 Subject: [PATCH 124/428] Correct OPFS xRead() and xWrite() impls and add a very basic sanity test for them. FossilOrigin-Name: cd06cc670029763955cf60ffcf944b36d41cb005b859d9b9fd0eea1b6741d0e9 --- ext/wasm/sqlite3-opfs-async-proxy.js | 6 +++--- ext/wasm/x-sync-async.js | 17 +++++++++++++---- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/ext/wasm/sqlite3-opfs-async-proxy.js b/ext/wasm/sqlite3-opfs-async-proxy.js index 4a60e8aa1a..58498e006b 100644 --- a/ext/wasm/sqlite3-opfs-async-proxy.js +++ b/ext/wasm/sqlite3-opfs-async-proxy.js @@ -259,10 +259,10 @@ const vfsAsyncImpls = { let rc = 0; const fh = __openFiles[fid]; try{ - const aRead = new Uint8array(fh.sab, n); + const aRead = new Uint8Array(fh.sab, 0, n); const nRead = fh.accessHandle.read(aRead, {at: offset}); if(nRead < n){/* Zero-fill remaining bytes */ - new Uint8array(fh.sab).fill(0, nRead, n); + new Uint8Array(fh.sab).fill(0, nRead, n); rc = state.sq3Codes.SQLITE_IOERR_SHORT_READ; } }catch(e){ @@ -302,7 +302,7 @@ const vfsAsyncImpls = { const fh = __openFiles[fid]; try{ affirmNotRO('xWrite', fh); - const nOut = fh.accessHandle.write(new UInt8Array(fh.sab, 0, n), {at: offset}); + const nOut = fh.accessHandle.write(new Uint8Array(fh.sab, 0, n), {at: offset}); rc = (nOut===n) ? 0 : state.sq3Codes.SQLITE_IOERR_WRITE; }catch(e){ error("xWrite():",e,fh); diff --git a/ext/wasm/x-sync-async.js b/ext/wasm/x-sync-async.js index 250fde349f..d36dc1700f 100644 --- a/ext/wasm/x-sync-async.js +++ b/ext/wasm/x-sync-async.js @@ -76,7 +76,7 @@ const initOpfsVfs = function(sqlite3){ of data may be added to it. */ const state = Object.create(null); - state.verbose = 3; + state.verbose = 2; state.fileBufferSize = 1024 * 64 + 8 /* size of fileHandle.sab. 64k = max sqlite3 page size */; state.fbInt64Offset = state.fileBufferSize - 8 /*spot in fileHandle.sab to store an int64*/; state.opIds = Object.create(null); @@ -239,10 +239,11 @@ const initOpfsVfs = function(sqlite3){ }, xRead: function(pFile,pDest,n,offset){ /* int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) */ - const f = __opfsHandles[pFile]; + const f = __openFiles[pFile]; + let rc; try { // FIXME(?): block until we finish copying the xRead result buffer. How? - let rc = opRun('xRead',{fid:pFile, n, offset}); + rc = opRun('xRead',{fid:pFile, n, offset}); if(0!==rc) return rc; let i = 0; for(; i < n; ++i) wasm.setMemValue(pDest + i, f.sabView[i]); @@ -265,7 +266,7 @@ const initOpfsVfs = function(sqlite3){ }, xWrite: function(pFile,pSrc,n,offset){ /* int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst) */ - const f = __opfsHandles[pFile]; + const f = __openFiles[pFile]; try { let i = 0; // FIXME(?): block from here until we finish the xWrite. How? @@ -429,6 +430,14 @@ const initOpfsVfs = function(sqlite3){ rc = ioSyncWrappers.xFileSize(sq3File.pointer, pOut); if(rc) toss('xFileSize failed w/ rc',rc); log("xFileSize says:",wasm.getMemValue(pOut, 'i64')); + rc = ioSyncWrappers.xWrite(sq3File.pointer, zDbFile, 10, 0); + if(rc) toss("xWrite() failed!"); + const readBuf = wasm.scopedAlloc(16); + rc = ioSyncWrappers.xRead(sq3File.pointer, readBuf, 6, 1); + wasm.setMemValue(readBuf+6,0); + let jRead = wasm.cstringToJs(readBuf); + log("xRead() got:",jRead); + if("sanity"!==jRead) toss("Unexpected xRead() value."); log("xSleep()ing before close()ing..."); opRun('xSleep',1000); rc = ioSyncWrappers.xClose(fid); diff --git a/manifest b/manifest index 503b895473..b288f12d6c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Implement\sOPFS\sxAccess(),\salbeit\swith\smore\slimited\ssemantics\sthan\sthe\sVFS\sAPI\scalls\sfor.\sAdd\sa\sway\sfor\sOPFS\sxDelete()\sto\soptionally\srecursively\sremove\sempty\sdirs\sleft\sover\safter\sdeleting\sa\sfile. -D 2022-09-17T23:29:27.832 +C Correct\sOPFS\sxRead()\sand\sxWrite()\simpls\sand\sadd\sa\svery\sbasic\ssanity\stest\sfor\sthem. +D 2022-09-17T23:47:20.619 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -523,7 +523,7 @@ F ext/wasm/speedtest1.html fbb8e4d1639028443f3687a683be660beca6927920545cf6b1fdf F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 -F ext/wasm/sqlite3-opfs-async-proxy.js c4be9e614abea5f237564757141e3e128f0cff4b4cd8350f68e17347230fd34e +F ext/wasm/sqlite3-opfs-async-proxy.js ef012be0a77d3e34dc699de2e430b6d26a58c69dbebdd75248d8f41540996c71 F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 @@ -534,7 +534,7 @@ F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c2 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c F ext/wasm/wasmfs.make 21a5cf297954a689e0dc2a95299ae158f681cae5e90c10b99d986097815fd42d F ext/wasm/x-sync-async.html 717b0d3bee96e49cbd36731bead497ab27a8bf3a3b23dd11e40e61d4ac9e8b80 -F ext/wasm/x-sync-async.js a95e8acb04fd69526317ee63bf25fa478a20e223d5dc16f7526baf5d81e07d1e +F ext/wasm/x-sync-async.js 961eb6c268086babd20d7b5e07cad72dc3118f9c46954e9a73b8b826deb4f771 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2030,8 +2030,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f36bddbe54c3acbfaa958042e4d24724f130bdca551401033f9bc63f3da73492 -R 5aad77d6a3a0d677e9c84e63e20c4294 +P c342b5d745f301104c59851c753287ebbbe95a69a56cb522d376d0f3e352c30f +R 70b6b0622f3dd6730d580a81783f068d U stephan -Z 31fefe04f3f471aad99d738829acf9a8 +Z 5c4a37b98dc31ffc6cd63d317f5738d7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6035cd7f00..39390f941c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c342b5d745f301104c59851c753287ebbbe95a69a56cb522d376d0f3e352c30f \ No newline at end of file +cd06cc670029763955cf60ffcf944b36d41cb005b859d9b9fd0eea1b6741d0e9 \ No newline at end of file From 3e2823cbb6e46549229ac177152d048fca8df958 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 18 Sep 2022 00:16:12 +0000 Subject: [PATCH 125/428] Plug OPFS methods in to their sqlite3_vfs/io_methods counterparts. Add URL args to control debug output and running of sanity-checks in the OPFS init code. FossilOrigin-Name: a0e93ed20b2463606a63b03ce8ca41ec1fb22886db5c5c898ace86ba24636f70 --- ext/wasm/index.html | 2 +- ext/wasm/x-sync-async.html | 2 +- ext/wasm/x-sync-async.js | 104 ++++++++++++++++++++++++++++++------- manifest | 16 +++--- manifest.uuid | 2 +- 5 files changed, 95 insertions(+), 31 deletions(-) diff --git a/ext/wasm/index.html b/ext/wasm/index.html index a1cc194854..562bb9b5ef 100644 --- a/ext/wasm/index.html +++ b/ext/wasm/index.html @@ -50,7 +50,7 @@ worker due to an Emscripten limitation.
  • scratchpad-opfs-worker: experimenting with OPFS from a Worker thread (without WASMFS).
  • -
  • x-sync-async is an +
  • x-sync-async is an experiment in implementing a syncronous sqlite3 VFS proxy for a fully synchronous backend interface (namely OPFS), using SharedArrayBuffer and the Atomics APIs to regulate communication between the synchronous diff --git a/ext/wasm/x-sync-async.html b/ext/wasm/x-sync-async.html index ec0a6353b5..b83d93378c 100644 --- a/ext/wasm/x-sync-async.html +++ b/ext/wasm/x-sync-async.html @@ -17,6 +17,6 @@
    - + diff --git a/ext/wasm/x-sync-async.js b/ext/wasm/x-sync-async.js index d36dc1700f..203c8eece4 100644 --- a/ext/wasm/x-sync-async.js +++ b/ext/wasm/x-sync-async.js @@ -44,7 +44,6 @@ const initOpfsVfs = function(sqlite3){ const error = (...args)=>{ console.error(logPrefix,...args); }; - warn("This file is very much experimental and under construction.",self.location.pathname); if(self.window===self || !self.SharedArrayBuffer || @@ -56,7 +55,7 @@ const initOpfsVfs = function(sqlite3){ warn("This environment does not have OPFS support."); return; } - + warn("This file is very much experimental and under construction.",self.location.pathname); const capi = sqlite3.capi; const wasm = capi.wasm; const sqlite3_vfs = capi.sqlite3_vfs @@ -65,6 +64,8 @@ const initOpfsVfs = function(sqlite3){ || toss("Missing sqlite3.capi.sqlite3_file object."); const sqlite3_io_methods = capi.sqlite3_io_methods || toss("Missing sqlite3.capi.sqlite3_io_methods object."); + const StructBinder = sqlite3.StructBinder || toss("Missing sqlite3.StructBinder."); + const thisUrl = new URL(self.location.href); const W = new Worker("sqlite3-opfs-async-proxy.js"); const wMsg = (type,payload)=>W.postMessage({type,payload}); @@ -76,7 +77,7 @@ const initOpfsVfs = function(sqlite3){ of data may be added to it. */ const state = Object.create(null); - state.verbose = 2; + state.verbose = thisUrl.searchParams.has('opfs-verbose') ? 3 : 2; state.fileBufferSize = 1024 * 64 + 8 /* size of fileHandle.sab. 64k = max sqlite3 page size */; state.fbInt64Offset = state.fileBufferSize - 8 /*spot in fileHandle.sab to store an int64*/; state.opIds = Object.create(null); @@ -110,7 +111,6 @@ const initOpfsVfs = function(sqlite3){ }); const isWorkerErrCode = (n)=>!!state.sq3Codes._reverse[n]; - const opStore = (op,val=-1)=>Atomics.store(state.opSABView, state.opIds[op], val); const opWait = (op,val=-1)=>Atomics.wait(state.opSABView, state.opIds[op], val); @@ -128,12 +128,6 @@ const initOpfsVfs = function(sqlite3){ return Atomics.load(state.opSABView, state.opIds[op]); }; - const wait = (ms,value)=>{ - return new Promise((resolve)=>{ - setTimeout(()=>resolve(value), ms); - }); - }; - /** Generates a random ASCII string len characters long, intended for use as a temporary file name. @@ -194,6 +188,75 @@ const initOpfsVfs = function(sqlite3){ environment or the other when sqlite3_os_end() is called (_if_ it gets called at all in a wasm build, which is undefined). */ + + /** + Installs a StructBinder-bound function pointer member of the + given name and function in the given StructType target object. + It creates a WASM proxy for the given function and arranges for + that proxy to be cleaned up when tgt.dispose() is called. Throws + on the slightest hint of error (e.g. tgt is-not-a StructType, + name does not map to a struct-bound member, etc.). + + Returns a proxy for this function which is bound to tgt and takes + 2 args (name,func). That function returns the same thing, + permitting calls to be chained. + + If called with only 1 arg, it has no side effects but returns a + func with the same signature as described above. + */ + const installMethod = function callee(tgt, name, func){ + if(!(tgt instanceof StructBinder.StructType)){ + toss("Usage error: target object is-not-a StructType."); + } + if(1===arguments.length){ + return (n,f)=>callee(tgt,n,f); + } + if(!callee.argcProxy){ + callee.argcProxy = function(func,sig){ + return function(...args){ + if(func.length!==arguments.length){ + toss("Argument mismatch. Native signature is:",sig); + } + return func.apply(this, args); + } + }; + callee.removeFuncList = function(){ + if(this.ondispose.__removeFuncList){ + this.ondispose.__removeFuncList.forEach( + (v,ndx)=>{ + if('number'===typeof v){ + try{wasm.uninstallFunction(v)} + catch(e){/*ignore*/} + } + /* else it's a descriptive label for the next number in + the list. */ + } + ); + delete this.ondispose.__removeFuncList; + } + }; + }/*static init*/ + const sigN = tgt.memberSignature(name); + if(sigN.length<2){ + toss("Member",name," is not a function pointer. Signature =",sigN); + } + const memKey = tgt.memberKey(name); + //log("installMethod",tgt, name, sigN); + const fProxy = 1 + // We can remove this proxy middle-man once the VFS is working + ? callee.argcProxy(func, sigN) + : func; + const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true)); + tgt[memKey] = pFunc; + if(!tgt.ondispose) tgt.ondispose = []; + if(!tgt.ondispose.__removeFuncList){ + tgt.ondispose.push('ondispose.__removeFuncList handler', + callee.removeFuncList); + tgt.ondispose.__removeFuncList = []; + } + tgt.ondispose.__removeFuncList.push(memKey, pFunc); + return (n,f)=>callee(tgt, n, f); + }/*installMethod*/; /** Impls for the sqlite3_io_methods methods. Maintenance reminder: @@ -390,10 +453,11 @@ const initOpfsVfs = function(sqlite3){ vfsSyncWrappers.xSleep = (pVfs,ms)=>opRun('xSleep',ms); } - /* - TODO: plug in the above functions in to opfsVfs and opfsIoMethods. - Code for doing so is in api/sqlite3-api-opfs.js. - */ + /* Install the vfs/io_methods into their C-level shared instances... */ + let inst = installMethod(opfsIoMethods); + for(let k of Object.keys(ioSyncWrappers)) inst(k, ioSyncWrappers[k]); + inst = installMethod(opfsVfs); + for(let k of Object.keys(vfsSyncWrappers)) inst(k, vfsSyncWrappers[k]); const sanityCheck = async function(){ //state.ioBuf = new Uint8Array(state.sabIo); @@ -430,10 +494,10 @@ const initOpfsVfs = function(sqlite3){ rc = ioSyncWrappers.xFileSize(sq3File.pointer, pOut); if(rc) toss('xFileSize failed w/ rc',rc); log("xFileSize says:",wasm.getMemValue(pOut, 'i64')); - rc = ioSyncWrappers.xWrite(sq3File.pointer, zDbFile, 10, 0); + rc = ioSyncWrappers.xWrite(sq3File.pointer, zDbFile, 10, 1); if(rc) toss("xWrite() failed!"); const readBuf = wasm.scopedAlloc(16); - rc = ioSyncWrappers.xRead(sq3File.pointer, readBuf, 6, 1); + rc = ioSyncWrappers.xRead(sq3File.pointer, readBuf, 6, 2); wasm.setMemValue(readBuf+6,0); let jRead = wasm.cstringToJs(readBuf); log("xRead() got:",jRead); @@ -453,9 +517,8 @@ const initOpfsVfs = function(sqlite3){ } }; - W.onmessage = function({data}){ - log("Worker.onmessage:",data); + //log("Worker.onmessage:",data); switch(data.type){ case 'loaded': /*Pass our config and shared state on to the async worker.*/ @@ -476,8 +539,9 @@ const initOpfsVfs = function(sqlite3){ } capi.sqlite3_vfs_register.addReference(opfsVfs, opfsIoMethods); state.opSABView = new Int32Array(state.opSAB); - if(self.location && +self.location.port > 1024){ - log("Running sanity check for dev mode..."); + + if(thisUrl.searchParams.has('opfs-sanity-check')){ + warn("Running sanity checks because of opfs-sanity-check URL arg..."); sanityCheck(); } warn("End of (very incomplete) OPFS setup.", opfsVfs); diff --git a/manifest b/manifest index b288f12d6c..561605cbab 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Correct\sOPFS\sxRead()\sand\sxWrite()\simpls\sand\sadd\sa\svery\sbasic\ssanity\stest\sfor\sthem. -D 2022-09-17T23:47:20.619 +C Plug\sOPFS\smethods\sin\sto\stheir\ssqlite3_vfs/io_methods\scounterparts.\sAdd\sURL\sargs\sto\scontrol\sdebug\soutput\sand\srunning\sof\ssanity-checks\sin\sthe\sOPFS\sinit\scode. +D 2022-09-18T00:16:12.445 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -502,7 +502,7 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 -F ext/wasm/index.html 8365e47e2aff1829923f0481292948487b27a0755fde9c0d3ad15f7ea0118992 +F ext/wasm/index.html b8a47afa96d0c7ac3081c6d469e1fd7be6b87c64b94e4fb62e26984a1564cac4 F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f @@ -533,8 +533,8 @@ F ext/wasm/testing1.js 7cd8ab255c238b030d928755ae8e91e7d90a12f2ae601b1b8f7827aaa F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c F ext/wasm/wasmfs.make 21a5cf297954a689e0dc2a95299ae158f681cae5e90c10b99d986097815fd42d -F ext/wasm/x-sync-async.html 717b0d3bee96e49cbd36731bead497ab27a8bf3a3b23dd11e40e61d4ac9e8b80 -F ext/wasm/x-sync-async.js 961eb6c268086babd20d7b5e07cad72dc3118f9c46954e9a73b8b826deb4f771 +F ext/wasm/x-sync-async.html d85cb9b1ab398ef5a20ce64a689ce4bf9a347ffe69edd46c2d3dc57b869a8925 +F ext/wasm/x-sync-async.js 2cd04d73ddc515479cc2e4b9246d6da21b3f494020261d47470f4b710c84c0da F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2030,8 +2030,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P c342b5d745f301104c59851c753287ebbbe95a69a56cb522d376d0f3e352c30f -R 70b6b0622f3dd6730d580a81783f068d +P cd06cc670029763955cf60ffcf944b36d41cb005b859d9b9fd0eea1b6741d0e9 +R af48df928e44c11df9f0c9eb2cf8376e U stephan -Z 5c4a37b98dc31ffc6cd63d317f5738d7 +Z 07b2e0eb2359d701cc905c5402ed9c24 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 39390f941c..274fb7f0c1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cd06cc670029763955cf60ffcf944b36d41cb005b859d9b9fd0eea1b6741d0e9 \ No newline at end of file +a0e93ed20b2463606a63b03ce8ca41ec1fb22886db5c5c898ace86ba24636f70 \ No newline at end of file From c5313afea7bf38bbe98b5a472b16a8f3efda07c0 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 18 Sep 2022 02:35:30 +0000 Subject: [PATCH 126/428] Move the OPFS VFS bits back into api/sqlite3-api-opfs.js. Refactor the OPFS VFS init process to use a Promise-returning function which the client must call, as that eliminates any uncertainty about when the VFS (necessarily activated asynchronously) actually becomes available to the client. FossilOrigin-Name: 1c660970d0f62bcfd6e698a72b050d99972a1e39f45a5ac24194a190f8f78ab3 --- ext/wasm/GNUmakefile | 15 +- ext/wasm/api/sqlite3-api-opfs.js | 972 +++++++++++++++++++------------ ext/wasm/x-sync-async.js | 544 +---------------- manifest | 16 +- manifest.uuid | 2 +- 5 files changed, 636 insertions(+), 913 deletions(-) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index dc0ce3681f..20354a6b5f 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -139,14 +139,13 @@ EXPORTED_FUNCTIONS.api: $(EXPORTED_FUNCTIONS.api.in) $(MAKEFILE) cat $(EXPORTED_FUNCTIONS.api.in) > $@ CLEAN_FILES += EXPORTED_FUNCTIONS.api -sqlite3-api.jses := \ - $(dir.api)/sqlite3-api-prologue.js \ - $(dir.common)/whwasmutil.js \ - $(dir.jacc)/jaccwabyt.js \ - $(dir.api)/sqlite3-api-glue.js \ - $(dir.api)/sqlite3-api-oo1.js \ - $(dir.api)/sqlite3-api-worker1.js -#sqlite3-api.jses += $(dir.api)/sqlite3-api-opfs.js +sqlite3-api.jses := $(dir.api)/sqlite3-api-prologue.js +sqlite3-api.jses += $(dir.common)/whwasmutil.js +sqlite3-api.jses += $(dir.jacc)/jaccwabyt.js +sqlite3-api.jses += $(dir.api)/sqlite3-api-glue.js +sqlite3-api.jses += $(dir.api)/sqlite3-api-oo1.js +sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.js +sqlite3-api.jses += $(dir.api)/sqlite3-api-opfs.js sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js sqlite3-api.js := sqlite3-api.js diff --git a/ext/wasm/api/sqlite3-api-opfs.js b/ext/wasm/api/sqlite3-api-opfs.js index 3faf956c72..af1c6f7608 100644 --- a/ext/wasm/api/sqlite3-api-opfs.js +++ b/ext/wasm/api/sqlite3-api-opfs.js @@ -1,5 +1,5 @@ /* - 2022-07-22 + 2022-09-18 The author disclaims copyright to this source code. In place of a legal notice, here is a blessing: @@ -10,10 +10,30 @@ *********************************************************************** - This file contains extensions to the sqlite3 WASM API related to the - Origin-Private FileSystem (OPFS). It is intended to be appended to - the main JS deliverable somewhere after sqlite3-api-glue.js and - before sqlite3-api-cleanup.js. + This file holds the synchronous half of an sqlite3_vfs + implementation which proxies, in a synchronous fashion, the + asynchronous Origin-Private FileSystem (OPFS) APIs using a second + Worker, implemented in sqlite3-opfs-async-proxy.js. This file is + intended to be appended to the main sqlite3 JS deliverable somewhere + after sqlite3-api-glue.js and before sqlite3-api-cleanup.js. + +*/ + +'use strict'; +self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ +/** + sqlite3.installOpfsVfs() returns a Promise which, on success, installs + an sqlite3_vfs named "opfs", suitable for use with all sqlite3 APIs + which accept a VFS. It uses the Origin-Private FileSystem API for + all file storage. On error it is rejected with an exception + explaining the problem. Reasons for rejection include, but are + not limited to: + + - The counterpart Worker (see below) could not be loaded. + + - The environment does not support OPFS. That includes when + this function is called from the main window thread. + Significant notes and limitations: @@ -21,375 +41,587 @@ available in bleeding-edge versions of Chrome (v102+, noting that that number will increase as the OPFS API matures). - - The _synchronous_ family of OPFS features (which is what this API - requires) are only available in non-shared Worker threads. This - file tries to detect that case and becomes a no-op if those - features do not seem to be available. + - The OPFS features used here are only available in dedicated Worker + threads. This file tries to detect that case and becomes a no-op + if those features do not seem to be available. + + - It requires the SharedArrayBuffer and Atomics classes, and the + former is only available if the HTTP server emits the so-called + COOP and COEP response headers. These features are required for + proxying OPFS's synchronous API via the synchronous interface + required by the sqlite3_vfs API. + + - This function may only be called a single time and it must be + called from the client, as opposed to the library initialization, + in case the client requires a custom path for this API's + "counterpart": this function's argument is the relative URI to + this module's "asynchronous half". When called, this function removes + itself from the sqlite3 object. + + The argument may optionally be a plain object with the following + configuration options: + + - proxyUri: as described above + + - verbose (=2): an integer 0-3. 0 disables all logging, 1 enables + logging of errors. 2 enables logging of warnings and errors. 3 + additionally enables debugging info. + + - sanityChecks (=false): if true, some basic sanity tests are + run on the OPFS VFS API after it's initialized, before the + returned Promise resolves. + + On success, the Promise resolves to the top-most sqlite3 namespace + object. */ - -// FileSystemHandle -// FileSystemDirectoryHandle -// FileSystemFileHandle -// FileSystemFileHandle.prototype.createSyncAccessHandle -self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ - const warn = console.warn.bind(console), - error = console.error.bind(console); - if(self.window===self || !self.importScripts || !self.FileSystemFileHandle - || !self.FileSystemFileHandle.prototype.createSyncAccessHandle){ - warn("OPFS is not available in this environment."); - return; - }else if(!navigator.storage.getDirectory){ - warn("The OPFS VFS requires navigator.storage.getDirectory."); - }else if(!sqlite3.capi.wasm.bigIntEnabled){ - error("OPFS requires BigInt support but sqlite3.capi.wasm.bigIntEnabled is false."); - return; - } - //warn('self.FileSystemFileHandle =',self.FileSystemFileHandle); - //warn('self.FileSystemFileHandle.prototype =',self.FileSystemFileHandle.prototype); - const toss = (...args)=>{throw new Error(args.join(' '))}; - const capi = sqlite3.capi, - wasm = capi.wasm; - const sqlite3_vfs = capi.sqlite3_vfs - || toss("Missing sqlite3.capi.sqlite3_vfs object."); - const sqlite3_file = capi.sqlite3_file - || toss("Missing sqlite3.capi.sqlite3_file object."); - const sqlite3_io_methods = capi.sqlite3_io_methods - || toss("Missing sqlite3.capi.sqlite3_io_methods object."); - const StructBinder = sqlite3.StructBinder || toss("Missing sqlite3.StructBinder."); - const debug = console.debug.bind(console), - log = console.log.bind(console); - warn("UNDER CONSTRUCTION: setting up OPFS VFS..."); - - const pDVfs = capi.sqlite3_vfs_find(null)/*pointer to default VFS*/; - const dVfs = pDVfs - ? new sqlite3_vfs(pDVfs) - : null /* dVfs will be null when sqlite3 is built with - SQLITE_OS_OTHER. Though we cannot currently handle - that case, the hope is to eventually be able to. */; - const oVfs = new sqlite3_vfs(); - const oIom = new sqlite3_io_methods(); - oVfs.$iVersion = 2/*yes, two*/; - oVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; - oVfs.$mxPathname = 1024/*sure, why not?*/; - oVfs.$zName = wasm.allocCString("opfs"); - oVfs.ondispose = [ - '$zName', oVfs.$zName, - 'cleanup dVfs', ()=>(dVfs ? dVfs.dispose() : null) - ]; - if(dVfs){ - oVfs.$xSleep = dVfs.$xSleep; - oVfs.$xRandomness = dVfs.$xRandomness; - } - // All C-side memory of oVfs is zeroed out, but just to be explicit: - oVfs.$xDlOpen = oVfs.$xDlError = oVfs.$xDlSym = oVfs.$xDlClose = null; - - /** - Pedantic sidebar about oVfs.ondispose: the entries in that array - are items to clean up when oVfs.dispose() is called, but in this - environment it will never be called. The VFS instance simply - hangs around until the WASM module instance is cleaned up. We - "could" _hypothetically_ clean it up by "importing" an - sqlite3_os_end() impl into the wasm build, but the shutdown order - of the wasm engine and the JS one are undefined so there is no - guaranty that the oVfs instance would be available in one - environment or the other when sqlite3_os_end() is called (_if_ it - gets called at all in a wasm build, which is undefined). - */ - - /** - Installs a StructBinder-bound function pointer member of the - given name and function in the given StructType target object. - It creates a WASM proxy for the given function and arranges for - that proxy to be cleaned up when tgt.dispose() is called. Throws - on the slightest hint of error (e.g. tgt is-not-a StructType, - name does not map to a struct-bound member, etc.). - - Returns a proxy for this function which is bound to tgt and takes - 2 args (name,func). That function returns the same thing, - permitting calls to be chained. - - If called with only 1 arg, it has no side effects but returns a - func with the same signature as described above. - */ - const installMethod = function callee(tgt, name, func){ - if(!(tgt instanceof StructBinder.StructType)){ - toss("Usage error: target object is-not-a StructType."); - } - if(1===arguments.length){ - return (n,f)=>callee(tgt,n,f); - } - if(!callee.argcProxy){ - callee.argcProxy = function(func,sig){ - return function(...args){ - if(func.length!==arguments.length){ - toss("Argument mismatch. Native signature is:",sig); - } - return func.apply(this, args); - } - }; - callee.removeFuncList = function(){ - if(this.ondispose.__removeFuncList){ - this.ondispose.__removeFuncList.forEach( - (v,ndx)=>{ - if('number'===typeof v){ - try{wasm.uninstallFunction(v)} - catch(e){/*ignore*/} - } - /* else it's a descriptive label for the next number in - the list. */ - } - ); - delete this.ondispose.__removeFuncList; - } - }; - }/*static init*/ - const sigN = tgt.memberSignature(name); - if(sigN.length<2){ - toss("Member",name," is not a function pointer. Signature =",sigN); - } - const memKey = tgt.memberKey(name); - //log("installMethod",tgt, name, sigN); - const fProxy = 1 - // We can remove this proxy middle-man once the VFS is working - ? callee.argcProxy(func, sigN) - : func; - const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true)); - tgt[memKey] = pFunc; - if(!tgt.ondispose) tgt.ondispose = []; - if(!tgt.ondispose.__removeFuncList){ - tgt.ondispose.push('ondispose.__removeFuncList handler', - callee.removeFuncList); - tgt.ondispose.__removeFuncList = []; - } - tgt.ondispose.__removeFuncList.push(memKey, pFunc); - return (n,f)=>callee(tgt, n, f); - }/*installMethod*/; - - /** - Map of sqlite3_file pointers to OPFS handles. - */ - const __opfsHandles = Object.create(null); - - const randomFilename = function f(len=16){ - if(!f._chars){ - f._chars = "abcdefghijklmnopqrstuvwxyz"+ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ - "012346789"; - f._n = f._chars.length; - } - const a = []; - let i = 0; - for( ; i < len; ++i){ - const ndx = Math.random() * (f._n * 64) % f._n | 0; - a[i] = f._chars[ndx]; - } - return a.join(''); +sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri){ + const options = (asyncProxyUri && 'object'===asyncProxyUri) ? asyncProxyUri : { + proxyUri: asyncProxyUri }; - - //const rootDir = await navigator.storage.getDirectory(); - - //////////////////////////////////////////////////////////////////////// - // Set up OPFS VFS methods... - let inst = installMethod(oVfs); - inst('xOpen', function(pVfs, zName, pFile, flags, pOutFlags){ - const f = new sqlite3_file(pFile); - f.$pMethods = oIom.pointer; - __opfsHandles[pFile] = f; - f.opfsHandle = null /* TODO */; - if(flags & capi.SQLITE_OPEN_DELETEONCLOSE){ - f.deleteOnClose = true; - } - f.filename = zName ? wasm.cstringToJs(zName) : randomFilename(); - error("OPFS sqlite3_vfs::xOpen is not yet full implemented."); - return capi.SQLITE_IOERR; - }) - ('xFullPathname', function(pVfs,zName,nOut,pOut){ - /* Until/unless we have some notion of "current dir" - in OPFS, simply copy zName to pOut... */ - const i = wasm.cstrncpy(pOut, zName, nOut); - return i{ + if(options.verbose>1) console.warn(logPrefix,...args); + }; + if(self.window===self || + !self.SharedArrayBuffer || + !self.FileSystemHandle || + !self.FileSystemDirectoryHandle || + !self.FileSystemFileHandle || + !self.FileSystemFileHandle.prototype.createSyncAccessHandle || + !navigator.storage.getDirectory){ + warn("This environment does not have OPFS support."); + promiseReject(new Error("This environment does not have OPFS support.")); + return; + } + warn("The OPFS VFS feature is very much experimental and under construction."); + const toss = function(...args){throw new Error(args.join(' '))}; + const log = (...args)=>{ + if(options.verbose>2) console.log(logPrefix,...args); + }; + const error = (...args)=>{ + if(options.verbose>0) console.error(logPrefix,...args); + }; + const capi = sqlite3.capi; + const wasm = capi.wasm; + const sqlite3_vfs = capi.sqlite3_vfs; + const sqlite3_file = capi.sqlite3_file; + const sqlite3_io_methods = capi.sqlite3_io_methods; + const StructBinder = sqlite3.StructBinder; + const W = new Worker(options.proxyUri); + const workerOrigOnError = W.onrror; + W.onerror = function(err){ + promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons.")); + }; + const wMsg = (type,payload)=>W.postMessage({type,payload}); + + /** + State which we send to the async-api Worker or share with it. + This object must initially contain only cloneable or sharable + objects. After the worker's "inited" message arrives, other types + of data may be added to it. + */ + const state = Object.create(null); + state.verbose = options.verbose; + state.fileBufferSize = 1024 * 64 + 8 /* size of fileHandle.sab. 64k = max sqlite3 page size */; + state.fbInt64Offset = state.fileBufferSize - 8 /*spot in fileHandle.sab to store an int64*/; + state.opIds = Object.create(null); + { let i = 0; - for(; i < nOut; ++i) heap[pOut + i] = (Math.random()*255000) & 0xFF; - return i; + state.opIds.xAccess = i++; + state.opIds.xClose = i++; + state.opIds.xDelete = i++; + state.opIds.xFileSize = i++; + state.opIds.xOpen = i++; + state.opIds.xRead = i++; + state.opIds.xSleep = i++; + state.opIds.xSync = i++; + state.opIds.xTruncate = i++; + state.opIds.xWrite = i++; + state.opSAB = new SharedArrayBuffer(i * 4/*sizeof int32*/); + /* The approach of using a single SAB to serialize comms for all + instances may(?) lead to deadlock situations in multi-db + cases. We should probably have one SAB here with a single slot + for locking a per-file initialization step and then allocate a + separate SAB like the above one for each file. That will + require a bit of acrobatics but should be feasible. + */ + } + + state.sq3Codes = Object.create(null); + state.sq3Codes._reverse = Object.create(null); + [ // SQLITE_xxx constants to export to the async worker counterpart... + 'SQLITE_ERROR', 'SQLITE_IOERR', + 'SQLITE_NOTFOUND', 'SQLITE_MISUSE', + 'SQLITE_IOERR_READ', 'SQLITE_IOERR_SHORT_READ', + 'SQLITE_IOERR_WRITE', 'SQLITE_IOERR_FSYNC', + 'SQLITE_IOERR_TRUNCATE', 'SQLITE_IOERR_DELETE', + 'SQLITE_IOERR_ACCESS', 'SQLITE_IOERR_CLOSE' + ].forEach(function(k){ + state.sq3Codes[k] = capi[k] || toss("Maintenance required: not found:",k); + state.sq3Codes._reverse[capi[k]] = k; }); - } - //////////////////////////////////////////////////////////////////////// - // Set up OPFS sqlite3_io_methods... - inst = installMethod(oIom); - inst('xClose', async function(pFile){ - warn("xClose(",arguments,") uses await"); - const f = __opfsHandles[pFile]; - delete __opfsHandles[pFile]; - if(f.opfsHandle){ - await f.opfsHandle.close(); - if(f.deleteOnClose){ - // TODO - } - } - f.dispose(); - return 0; - }) - ('xRead', /*i(ppij)*/function(pFile,pDest,n,offset){ - /* int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) */ - try { - const f = __opfsHandles[pFile]; - const heap = wasm.heap8u(); - const b = new Uint8Array(heap.buffer, pDest, n); - const nRead = f.opfsHandle.read(b, {at: offset}); - if(nRead SQLITE_DEFAULT_SECTOR_SIZE */; - //}) + const isWorkerErrCode = (n)=>!!state.sq3Codes._reverse[n]; + const opStore = (op,val=-1)=>Atomics.store(state.opSABView, state.opIds[op], val); + const opWait = (op,val=-1)=>Atomics.wait(state.opSABView, state.opIds[op], val); - const rc = capi.sqlite3_vfs_register(oVfs.pointer, 0); - if(rc){ - oVfs.dispose(); - toss("sqlite3_vfs_register(OPFS) failed with rc",rc); - } - capi.sqlite3_vfs_register.addReference(oVfs, oIom); - warn("End of (very incomplete) OPFS setup.", oVfs); - //oVfs.dispose()/*only because we can't yet do anything with it*/; -}); + /** + Runs the given operation in the async worker counterpart, waits + for its response, and returns the result which the async worker + writes to the given op's index in state.opSABView. The 2nd argument + must be a single object or primitive value, depending on the + given operation's signature in the async API counterpart. + */ + const opRun = (op,args)=>{ + opStore(op); + wMsg(op, args); + opWait(op); + return Atomics.load(state.opSABView, state.opIds[op]); + }; + + /** + Generates a random ASCII string len characters long, intended for + use as a temporary file name. + */ + const randomFilename = function f(len=16){ + if(!f._chars){ + f._chars = "abcdefghijklmnopqrstuvwxyz"+ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ + "012346789"; + f._n = f._chars.length; + } + const a = []; + let i = 0; + for( ; i < len; ++i){ + const ndx = Math.random() * (f._n * 64) % f._n | 0; + a[i] = f._chars[ndx]; + } + return a.join(''); + }; + + /** + Map of sqlite3_file pointers to objects constructed by xOpen(). + */ + const __openFiles = Object.create(null); + + const pDVfs = capi.sqlite3_vfs_find(null)/*pointer to default VFS*/; + const dVfs = pDVfs + ? new sqlite3_vfs(pDVfs) + : null /* dVfs will be null when sqlite3 is built with + SQLITE_OS_OTHER. Though we cannot currently handle + that case, the hope is to eventually be able to. */; + const opfsVfs = new sqlite3_vfs(); + const opfsIoMethods = new sqlite3_io_methods(); + opfsVfs.$iVersion = 2/*yes, two*/; + opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; + opfsVfs.$mxPathname = 1024/*sure, why not?*/; + opfsVfs.$zName = wasm.allocCString("opfs"); + // All C-side memory of opfsVfs is zeroed out, but just to be explicit: + opfsVfs.$xDlOpen = opfsVfs.$xDlError = opfsVfs.$xDlSym = opfsVfs.$xDlClose = null; + opfsVfs.ondispose = [ + '$zName', opfsVfs.$zName, + 'cleanup default VFS wrapper', ()=>(dVfs ? dVfs.dispose() : null), + 'cleanup opfsIoMethods', ()=>opfsIoMethods.dispose() + ]; + if(dVfs){ + opfsVfs.$xSleep = dVfs.$xSleep; + opfsVfs.$xRandomness = dVfs.$xRandomness; + } + /** + Pedantic sidebar about opfsVfs.ondispose: the entries in that array + are items to clean up when opfsVfs.dispose() is called, but in this + environment it will never be called. The VFS instance simply + hangs around until the WASM module instance is cleaned up. We + "could" _hypothetically_ clean it up by "importing" an + sqlite3_os_end() impl into the wasm build, but the shutdown order + of the wasm engine and the JS one are undefined so there is no + guaranty that the opfsVfs instance would be available in one + environment or the other when sqlite3_os_end() is called (_if_ it + gets called at all in a wasm build, which is undefined). + */ + + /** + Installs a StructBinder-bound function pointer member of the + given name and function in the given StructType target object. + It creates a WASM proxy for the given function and arranges for + that proxy to be cleaned up when tgt.dispose() is called. Throws + on the slightest hint of error (e.g. tgt is-not-a StructType, + name does not map to a struct-bound member, etc.). + + Returns a proxy for this function which is bound to tgt and takes + 2 args (name,func). That function returns the same thing, + permitting calls to be chained. + + If called with only 1 arg, it has no side effects but returns a + func with the same signature as described above. + */ + const installMethod = function callee(tgt, name, func){ + if(!(tgt instanceof StructBinder.StructType)){ + toss("Usage error: target object is-not-a StructType."); + } + if(1===arguments.length){ + return (n,f)=>callee(tgt,n,f); + } + if(!callee.argcProxy){ + callee.argcProxy = function(func,sig){ + return function(...args){ + if(func.length!==arguments.length){ + toss("Argument mismatch. Native signature is:",sig); + } + return func.apply(this, args); + } + }; + callee.removeFuncList = function(){ + if(this.ondispose.__removeFuncList){ + this.ondispose.__removeFuncList.forEach( + (v,ndx)=>{ + if('number'===typeof v){ + try{wasm.uninstallFunction(v)} + catch(e){/*ignore*/} + } + /* else it's a descriptive label for the next number in + the list. */ + } + ); + delete this.ondispose.__removeFuncList; + } + }; + }/*static init*/ + const sigN = tgt.memberSignature(name); + if(sigN.length<2){ + toss("Member",name," is not a function pointer. Signature =",sigN); + } + const memKey = tgt.memberKey(name); + //log("installMethod",tgt, name, sigN); + const fProxy = 1 + // We can remove this proxy middle-man once the VFS is working + ? callee.argcProxy(func, sigN) + : func; + const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true)); + tgt[memKey] = pFunc; + if(!tgt.ondispose) tgt.ondispose = []; + if(!tgt.ondispose.__removeFuncList){ + tgt.ondispose.push('ondispose.__removeFuncList handler', + callee.removeFuncList); + tgt.ondispose.__removeFuncList = []; + } + tgt.ondispose.__removeFuncList.push(memKey, pFunc); + return (n,f)=>callee(tgt, n, f); + }/*installMethod*/; + + /** + Impls for the sqlite3_io_methods methods. Maintenance reminder: + members are in alphabetical order to simplify finding them. + */ + const ioSyncWrappers = { + xCheckReservedLock: function(pFile,pOut){ + // Exclusive lock is automatically acquired when opened + //warn("xCheckReservedLock(",arguments,") is a no-op"); + wasm.setMemValue(pOut,1,'i32'); + return 0; + }, + xClose: function(pFile){ + let rc = 0; + const f = __openFiles[pFile]; + if(f){ + delete __openFiles[pFile]; + rc = opRun('xClose', pFile); + if(f.sq3File) f.sq3File.dispose(); + } + return rc; + }, + xDeviceCharacteristics: function(pFile){ + //debug("xDeviceCharacteristics(",pFile,")"); + return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; + }, + xFileControl: function(pFile,op,pArg){ + //debug("xFileControl(",arguments,") is a no-op"); + return capi.SQLITE_NOTFOUND; + }, + xFileSize: function(pFile,pSz64){ + const rc = opRun('xFileSize', pFile); + if(!isWorkerErrCode(rc)){ + const f = __openFiles[pFile]; + wasm.setMemValue(pSz64, f.sabViewFileSize.getBigInt64(0) ,'i64'); + } + return rc; + }, + xLock: function(pFile,lockType){ + //2022-09: OPFS handles lock when opened + //warn("xLock(",arguments,") is a no-op"); + return 0; + }, + xRead: function(pFile,pDest,n,offset){ + /* int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) */ + const f = __openFiles[pFile]; + let rc; + try { + // FIXME(?): block until we finish copying the xRead result buffer. How? + rc = opRun('xRead',{fid:pFile, n, offset}); + if(0!==rc) return rc; + let i = 0; + for(; i < n; ++i) wasm.setMemValue(pDest + i, f.sabView[i]); + }catch(e){ + error("xRead(",arguments,") failed:",e,f); + rc = capi.SQLITE_IOERR_READ; + } + return rc; + }, + xSync: function(pFile,flags){ + return opRun('xSync', {fid:pFile, flags}); + }, + xTruncate: function(pFile,sz64){ + return opRun('xTruncate', {fid:pFile, size: sz64}); + }, + xUnlock: function(pFile,lockType){ + //2022-09: OPFS handles lock when opened + //warn("xUnlock(",arguments,") is a no-op"); + return 0; + }, + xWrite: function(pFile,pSrc,n,offset){ + /* int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst) */ + const f = __openFiles[pFile]; + try { + let i = 0; + // FIXME(?): block from here until we finish the xWrite. How? + for(; i < n; ++i) f.sabView[i] = wasm.getMemValue(pSrc+i); + return opRun('xWrite',{fid:pFile, n, offset}); + }catch(e){ + error("xWrite(",arguments,") failed:",e,f); + return capi.SQLITE_IOERR_WRITE; + } + } + }/*ioSyncWrappers*/; + + /** + Impls for the sqlite3_vfs methods. Maintenance reminder: members + are in alphabetical order to simplify finding them. + */ + const vfsSyncWrappers = { + xAccess: function(pVfs,zName,flags,pOut){ + const rc = opRun('xAccess', wasm.cstringToJs(zName)); + wasm.setMemValue(pOut, rc ? 0 : 1, 'i32'); + return 0; + }, + xCurrentTime: function(pVfs,pOut){ + /* If it turns out that we need to adjust for timezone, see: + https://stackoverflow.com/a/11760121/1458521 */ + wasm.setMemValue(pOut, 2440587.5 + (new Date().getTime()/86400000), + 'double'); + return 0; + }, + xCurrentTimeInt64: function(pVfs,pOut){ + // TODO: confirm that this calculation is correct + wasm.setMemValue(pOut, (2440587.5 * 86400000) + new Date().getTime(), + 'i64'); + return 0; + }, + xDelete: function(pVfs, zName, doSyncDir){ + return opRun('xDelete', {filename: wasm.cstringToJs(zName), syncDir: doSyncDir}); + }, + xFullPathname: function(pVfs,zName,nOut,pOut){ + /* Until/unless we have some notion of "current dir" + in OPFS, simply copy zName to pOut... */ + const i = wasm.cstrncpy(pOut, zName, nOut); + return ipMethods is NULL. */ + if(args.readOnly){ + wasm.setMemValue(pOutFlags, capi.SQLITE_OPEN_READONLY, 'i32'); + } + __openFiles[pFile] = args; + args.sabView = new Uint8Array(args.sab); + args.sabViewFileSize = new DataView(args.sab, state.fbInt64Offset, 8); + args.sq3File = new sqlite3_file(pFile); + args.sq3File.$pMethods = opfsIoMethods.pointer; + args.ba = new Uint8Array(args.sab); + } + return rc; + }/*xOpen()*/ + }/*vfsSyncWrappers*/; + + if(!opfsVfs.$xRandomness){ + /* If the default VFS has no xRandomness(), add a basic JS impl... */ + vfsSyncWrappers.xRandomness = function(pVfs, nOut, pOut){ + const heap = wasm.heap8u(); + let i = 0; + for(; i < nOut; ++i) heap[pOut + i] = (Math.random()*255000) & 0xFF; + return i; + }; + } + if(!opfsVfs.$xSleep){ + /* If we can inherit an xSleep() impl from the default VFS then + use it, otherwise install one which is certainly less accurate + because it has to go round-trip through the async worker, but + provides the only option for a synchronous sleep() in JS. */ + vfsSyncWrappers.xSleep = (pVfs,ms)=>opRun('xSleep',ms); + } + + /* Install the vfs/io_methods into their C-level shared instances... */ + let inst = installMethod(opfsIoMethods); + for(let k of Object.keys(ioSyncWrappers)) inst(k, ioSyncWrappers[k]); + inst = installMethod(opfsVfs); + for(let k of Object.keys(vfsSyncWrappers)) inst(k, vfsSyncWrappers[k]); + + const sanityCheck = async function(){ + const scope = wasm.scopedAllocPush(); + const sq3File = new sqlite3_file(); + try{ + const fid = sq3File.pointer; + const openFlags = capi.SQLITE_OPEN_CREATE + | capi.SQLITE_OPEN_READWRITE + //| capi.SQLITE_OPEN_DELETEONCLOSE + | capi.SQLITE_OPEN_MAIN_DB; + const pOut = wasm.scopedAlloc(8); + const dbFile = "/sanity/check/file"; + const zDbFile = wasm.scopedAllocCString(dbFile); + let rc; + vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.getMemValue(pOut,'i32'); + log("xAccess(",dbFile,") exists ?=",rc); + rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile, + fid, openFlags, pOut); + log("open rc =",rc,"state.opSABView[xOpen] =",state.opSABView[state.opIds.xOpen]); + if(isWorkerErrCode(rc)){ + error("open failed with code",rc); + return; + } + vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.getMemValue(pOut,'i32'); + if(!rc) toss("xAccess() failed to detect file."); + rc = ioSyncWrappers.xSync(sq3File.pointer, 0); + if(rc) toss('sync failed w/ rc',rc); + rc = ioSyncWrappers.xTruncate(sq3File.pointer, 1024); + if(rc) toss('truncate failed w/ rc',rc); + wasm.setMemValue(pOut,0,'i64'); + rc = ioSyncWrappers.xFileSize(sq3File.pointer, pOut); + if(rc) toss('xFileSize failed w/ rc',rc); + log("xFileSize says:",wasm.getMemValue(pOut, 'i64')); + rc = ioSyncWrappers.xWrite(sq3File.pointer, zDbFile, 10, 1); + if(rc) toss("xWrite() failed!"); + const readBuf = wasm.scopedAlloc(16); + rc = ioSyncWrappers.xRead(sq3File.pointer, readBuf, 6, 2); + wasm.setMemValue(readBuf+6,0); + let jRead = wasm.cstringToJs(readBuf); + log("xRead() got:",jRead); + if("sanity"!==jRead) toss("Unexpected xRead() value."); + log("xSleep()ing before close()ing..."); + opRun('xSleep',1000); + rc = ioSyncWrappers.xClose(fid); + log("xClose rc =",rc,"opSABView =",state.opSABView); + log("Deleting file:",dbFile); + vfsSyncWrappers.xDelete(opfsVfs.pointer, zDbFile, 0x1234); + vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); + rc = wasm.getMemValue(pOut,'i32'); + if(rc) toss("Expecting 0 from xAccess(",dbFile,") after xDelete()."); + }finally{ + sq3File.dispose(); + wasm.scopedAllocPop(scope); + } + }/*sanityCheck()*/; + + W.onmessage = function({data}){ + //log("Worker.onmessage:",data); + switch(data.type){ + case 'loaded': + /*Pass our config and shared state on to the async worker.*/ + wMsg('init',state); + break; + case 'inited':{ + /*Indicates that the async partner has received the 'init', + so we now know that the state object is no longer subject to + being copied by a pending postMessage() call.*/ + try { + const rc = capi.sqlite3_vfs_register(opfsVfs.pointer, opfsVfs.$zName); + if(rc){ + opfsVfs.dispose(); + toss("sqlite3_vfs_register(OPFS) failed with rc",rc); + } + if(opfsVfs.pointer !== capi.sqlite3_vfs_find("opfs")){ + toss("BUG: sqlite3_vfs_find() failed for just-installed OPFS VFS"); + } + capi.sqlite3_vfs_register.addReference(opfsVfs, opfsIoMethods); + state.opSABView = new Int32Array(state.opSAB); + if(options.sanityChecks){ + warn("Running sanity checks because of opfs-sanity-check URL arg..."); + sanityCheck(); + } + W.onerror = workerOrigOnError; + promiseResolve(sqlite3); + log("End of OPFS sqlite3_vfs setup.", opfsVfs); + }catch(e){ + error(e); + promiseReject(e); + } + break; + } + default: + promiseReject(e); + error("Unexpected message from the async worker:",data); + break; + } + }; + })/*thePromise*/; + return thePromise; +}/*installOpfsVfs()*/; +sqlite3.installOpfsVfs.defaultProxyUri = "sqlite3-opfs-async-proxy.js"; +}/*sqlite3ApiBootstrap.initializers.push()*/); diff --git a/ext/wasm/x-sync-async.js b/ext/wasm/x-sync-async.js index 203c8eece4..b25f5a2681 100644 --- a/ext/wasm/x-sync-async.js +++ b/ext/wasm/x-sync-async.js @@ -10,11 +10,7 @@ *********************************************************************** - An INCOMPLETE and UNDER CONSTRUCTION experiment for OPFS. - This file holds the synchronous half of an sqlite3_vfs - implementation which proxies, in a synchronous fashion, the - asynchronous OPFS APIs using a second Worker, implemented - in sqlite3-opfs-async-proxy.js. + A testing ground for the OPFS VFS. Summary of how this works: @@ -23,18 +19,12 @@ conventional sqlite3_vfs (except that it's implemented in JS). The methods which require OPFS APIs use a separate worker (hereafter called the OPFS worker) to access that functionality. This worker and that one - use SharedBufferArray + use SharedArrayBuffer. */ 'use strict'; -/** - This function is a placeholder for use in development. When - working, this will be moved into a file named - api/sqlite3-api-opfs.js, or similar, and hooked in to the - sqlite-api build construct. -*/ -const initOpfsVfs = function(sqlite3){ +const tryOpfsVfs = function(sqlite3){ const toss = function(...args){throw new Error(args.join(' '))}; - const logPrefix = "OPFS syncer:"; + const logPrefix = "OPFS tester:"; const log = (...args)=>{ console.log(logPrefix,...args); }; @@ -44,518 +34,20 @@ const initOpfsVfs = function(sqlite3){ const error = (...args)=>{ console.error(logPrefix,...args); }; - - if(self.window===self || - !self.SharedArrayBuffer || - !self.FileSystemHandle || - !self.FileSystemDirectoryHandle || - !self.FileSystemFileHandle || - !self.FileSystemFileHandle.prototype.createSyncAccessHandle || - !navigator.storage.getDirectory){ - warn("This environment does not have OPFS support."); - return; - } - warn("This file is very much experimental and under construction.",self.location.pathname); + log("tryOpfsVfs()"); const capi = sqlite3.capi; - const wasm = capi.wasm; - const sqlite3_vfs = capi.sqlite3_vfs - || toss("Missing sqlite3.capi.sqlite3_vfs object."); - const sqlite3_file = capi.sqlite3_file - || toss("Missing sqlite3.capi.sqlite3_file object."); - const sqlite3_io_methods = capi.sqlite3_io_methods - || toss("Missing sqlite3.capi.sqlite3_io_methods object."); - const StructBinder = sqlite3.StructBinder || toss("Missing sqlite3.StructBinder."); - const thisUrl = new URL(self.location.href); - - const W = new Worker("sqlite3-opfs-async-proxy.js"); - const wMsg = (type,payload)=>W.postMessage({type,payload}); - - /** - State which we send to the async-api Worker or share with it. - This object must initially contain only cloneable or sharable - objects. After the worker's "inited" message arrives, other types - of data may be added to it. - */ - const state = Object.create(null); - state.verbose = thisUrl.searchParams.has('opfs-verbose') ? 3 : 2; - state.fileBufferSize = 1024 * 64 + 8 /* size of fileHandle.sab. 64k = max sqlite3 page size */; - state.fbInt64Offset = state.fileBufferSize - 8 /*spot in fileHandle.sab to store an int64*/; - state.opIds = Object.create(null); - { - let i = 0; - state.opIds.xAccess = i++; - state.opIds.xClose = i++; - state.opIds.xDelete = i++; - state.opIds.xFileSize = i++; - state.opIds.xOpen = i++; - state.opIds.xRead = i++; - state.opIds.xSleep = i++; - state.opIds.xSync = i++; - state.opIds.xTruncate = i++; - state.opIds.xWrite = i++; - state.opSAB = new SharedArrayBuffer(i * 4/*sizeof int32*/); - } - - state.sq3Codes = Object.create(null); - state.sq3Codes._reverse = Object.create(null); - [ // SQLITE_xxx constants to export to the async worker counterpart... - 'SQLITE_ERROR', 'SQLITE_IOERR', - 'SQLITE_NOTFOUND', 'SQLITE_MISUSE', - 'SQLITE_IOERR_READ', 'SQLITE_IOERR_SHORT_READ', - 'SQLITE_IOERR_WRITE', 'SQLITE_IOERR_FSYNC', - 'SQLITE_IOERR_TRUNCATE', 'SQLITE_IOERR_DELETE', - 'SQLITE_IOERR_ACCESS', 'SQLITE_IOERR_CLOSE' - ].forEach(function(k){ - state.sq3Codes[k] = capi[k] || toss("Maintenance required: not found:",k); - state.sq3Codes._reverse[capi[k]] = k; - }); - - const isWorkerErrCode = (n)=>!!state.sq3Codes._reverse[n]; - const opStore = (op,val=-1)=>Atomics.store(state.opSABView, state.opIds[op], val); - const opWait = (op,val=-1)=>Atomics.wait(state.opSABView, state.opIds[op], val); - - /** - Runs the given operation in the async worker counterpart, waits - for its response, and returns the result which the async worker - writes to the given op's index in state.opSABView. The 2nd argument - must be a single object or primitive value, depending on the - given operation's signature in the async API counterpart. - */ - const opRun = (op,args)=>{ - opStore(op); - wMsg(op, args); - opWait(op); - return Atomics.load(state.opSABView, state.opIds[op]); - }; - - /** - Generates a random ASCII string len characters long, intended for - use as a temporary file name. - */ - const randomFilename = function f(len=16){ - if(!f._chars){ - f._chars = "abcdefghijklmnopqrstuvwxyz"+ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ - "012346789"; - f._n = f._chars.length; - } - const a = []; - let i = 0; - for( ; i < len; ++i){ - const ndx = Math.random() * (f._n * 64) % f._n | 0; - a[i] = f._chars[ndx]; - } - return a.join(''); - }; - - /** - Map of sqlite3_file pointers to objects constructed by xOpen(). - */ - const __openFiles = Object.create(null); - - const pDVfs = capi.sqlite3_vfs_find(null)/*pointer to default VFS*/; - const dVfs = pDVfs - ? new sqlite3_vfs(pDVfs) - : null /* dVfs will be null when sqlite3 is built with - SQLITE_OS_OTHER. Though we cannot currently handle - that case, the hope is to eventually be able to. */; - const opfsVfs = new sqlite3_vfs(); - const opfsIoMethods = new sqlite3_io_methods(); - opfsVfs.$iVersion = 2/*yes, two*/; - opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; - opfsVfs.$mxPathname = 1024/*sure, why not?*/; - opfsVfs.$zName = wasm.allocCString("opfs"); - // All C-side memory of opfsVfs is zeroed out, but just to be explicit: - opfsVfs.$xDlOpen = opfsVfs.$xDlError = opfsVfs.$xDlSym = opfsVfs.$xDlClose = null; - opfsVfs.ondispose = [ - '$zName', opfsVfs.$zName, - 'cleanup default VFS wrapper', ()=>(dVfs ? dVfs.dispose() : null), - 'cleanup opfsIoMethods', ()=>opfsIoMethods.dispose() - ]; - if(dVfs){ - opfsVfs.$xSleep = dVfs.$xSleep; - opfsVfs.$xRandomness = dVfs.$xRandomness; - } - /** - Pedantic sidebar about opfsVfs.ondispose: the entries in that array - are items to clean up when opfsVfs.dispose() is called, but in this - environment it will never be called. The VFS instance simply - hangs around until the WASM module instance is cleaned up. We - "could" _hypothetically_ clean it up by "importing" an - sqlite3_os_end() impl into the wasm build, but the shutdown order - of the wasm engine and the JS one are undefined so there is no - guaranty that the opfsVfs instance would be available in one - environment or the other when sqlite3_os_end() is called (_if_ it - gets called at all in a wasm build, which is undefined). - */ - - /** - Installs a StructBinder-bound function pointer member of the - given name and function in the given StructType target object. - It creates a WASM proxy for the given function and arranges for - that proxy to be cleaned up when tgt.dispose() is called. Throws - on the slightest hint of error (e.g. tgt is-not-a StructType, - name does not map to a struct-bound member, etc.). - - Returns a proxy for this function which is bound to tgt and takes - 2 args (name,func). That function returns the same thing, - permitting calls to be chained. - - If called with only 1 arg, it has no side effects but returns a - func with the same signature as described above. - */ - const installMethod = function callee(tgt, name, func){ - if(!(tgt instanceof StructBinder.StructType)){ - toss("Usage error: target object is-not-a StructType."); - } - if(1===arguments.length){ - return (n,f)=>callee(tgt,n,f); - } - if(!callee.argcProxy){ - callee.argcProxy = function(func,sig){ - return function(...args){ - if(func.length!==arguments.length){ - toss("Argument mismatch. Native signature is:",sig); - } - return func.apply(this, args); - } - }; - callee.removeFuncList = function(){ - if(this.ondispose.__removeFuncList){ - this.ondispose.__removeFuncList.forEach( - (v,ndx)=>{ - if('number'===typeof v){ - try{wasm.uninstallFunction(v)} - catch(e){/*ignore*/} - } - /* else it's a descriptive label for the next number in - the list. */ - } - ); - delete this.ondispose.__removeFuncList; - } - }; - }/*static init*/ - const sigN = tgt.memberSignature(name); - if(sigN.length<2){ - toss("Member",name," is not a function pointer. Signature =",sigN); - } - const memKey = tgt.memberKey(name); - //log("installMethod",tgt, name, sigN); - const fProxy = 1 - // We can remove this proxy middle-man once the VFS is working - ? callee.argcProxy(func, sigN) - : func; - const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true)); - tgt[memKey] = pFunc; - if(!tgt.ondispose) tgt.ondispose = []; - if(!tgt.ondispose.__removeFuncList){ - tgt.ondispose.push('ondispose.__removeFuncList handler', - callee.removeFuncList); - tgt.ondispose.__removeFuncList = []; - } - tgt.ondispose.__removeFuncList.push(memKey, pFunc); - return (n,f)=>callee(tgt, n, f); - }/*installMethod*/; - - /** - Impls for the sqlite3_io_methods methods. Maintenance reminder: - members are in alphabetical order to simplify finding them. - */ - const ioSyncWrappers = { - xCheckReservedLock: function(pFile,pOut){ - // Exclusive lock is automatically acquired when opened - //warn("xCheckReservedLock(",arguments,") is a no-op"); - wasm.setMemValue(pOut,1,'i32'); - return 0; - }, - xClose: function(pFile){ - let rc = 0; - const f = __openFiles[pFile]; - if(f){ - delete __openFiles[pFile]; - rc = opRun('xClose', pFile); - if(f.sq3File) f.sq3File.dispose(); - } - return rc; - }, - xDeviceCharacteristics: function(pFile){ - //debug("xDeviceCharacteristics(",pFile,")"); - return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; - }, - xFileControl: function(pFile,op,pArg){ - //debug("xFileControl(",arguments,") is a no-op"); - return capi.SQLITE_NOTFOUND; - }, - xFileSize: function(pFile,pSz64){ - const rc = opRun('xFileSize', pFile); - if(!isWorkerErrCode(rc)){ - const f = __openFiles[pFile]; - wasm.setMemValue(pSz64, f.sabViewFileSize.getBigInt64(0) ,'i64'); - } - return rc; - }, - xLock: function(pFile,lockType){ - //2022-09: OPFS handles lock when opened - //warn("xLock(",arguments,") is a no-op"); - return 0; - }, - xRead: function(pFile,pDest,n,offset){ - /* int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) */ - const f = __openFiles[pFile]; - let rc; - try { - // FIXME(?): block until we finish copying the xRead result buffer. How? - rc = opRun('xRead',{fid:pFile, n, offset}); - if(0!==rc) return rc; - let i = 0; - for(; i < n; ++i) wasm.setMemValue(pDest + i, f.sabView[i]); - }catch(e){ - error("xRead(",arguments,") failed:",e,f); - rc = capi.SQLITE_IOERR_READ; - } - return rc; - }, - xSync: function(pFile,flags){ - return opRun('xSync', {fid:pFile, flags}); - }, - xTruncate: function(pFile,sz64){ - return opRun('xTruncate', {fid:pFile, size: sz64}); - }, - xUnlock: function(pFile,lockType){ - //2022-09: OPFS handles lock when opened - //warn("xUnlock(",arguments,") is a no-op"); - return 0; - }, - xWrite: function(pFile,pSrc,n,offset){ - /* int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst) */ - const f = __openFiles[pFile]; - try { - let i = 0; - // FIXME(?): block from here until we finish the xWrite. How? - for(; i < n; ++i) f.sabView[i] = wasm.getMemValue(pSrc+i); - return opRun('xWrite',{fid:pFile, n, offset}); - }catch(e){ - error("xWrite(",arguments,") failed:",e,f); - return capi.SQLITE_IOERR_WRITE; - } - } - }/*ioSyncWrappers*/; - - /** - Impls for the sqlite3_vfs methods. Maintenance reminder: members - are in alphabetical order to simplify finding them. - */ - const vfsSyncWrappers = { - xAccess: function(pVfs,zName,flags,pOut){ - const rc = opRun('xAccess', wasm.cstringToJs(zName)); - wasm.setMemValue(pOut, rc ? 0 : 1, 'i32'); - return 0; - }, - xCurrentTime: function(pVfs,pOut){ - /* If it turns out that we need to adjust for timezone, see: - https://stackoverflow.com/a/11760121/1458521 */ - wasm.setMemValue(pOut, 2440587.5 + (new Date().getTime()/86400000), - 'double'); - return 0; - }, - xCurrentTimeInt64: function(pVfs,pOut){ - // TODO: confirm that this calculation is correct - wasm.setMemValue(pOut, (2440587.5 * 86400000) + new Date().getTime(), - 'i64'); - return 0; - }, - xDelete: function(pVfs, zName, doSyncDir){ - return opRun('xDelete', {filename: wasm.cstringToJs(zName), syncDir: doSyncDir}); - }, - xFullPathname: function(pVfs,zName,nOut,pOut){ - /* Until/unless we have some notion of "current dir" - in OPFS, simply copy zName to pOut... */ - const i = wasm.cstrncpy(pOut, zName, nOut); - return ipMethods is NULL. */ - if(args.readOnly){ - wasm.setMemValue(pOutFlags, capi.SQLITE_OPEN_READONLY, 'i32'); - } - __openFiles[pFile] = args; - args.sabView = new Uint8Array(args.sab); - args.sabViewFileSize = new DataView(args.sab, state.fbInt64Offset, 8); - args.sq3File = new sqlite3_file(pFile); - args.sq3File.$pMethods = opfsIoMethods.pointer; - args.ba = new Uint8Array(args.sab); - } - return rc; - }/*xOpen()*/ - }/*vfsSyncWrappers*/; - - if(!opfsVfs.$xRandomness){ - /* If the default VFS has no xRandomness(), add a basic JS impl... */ - vfsSyncWrappers.xRandomness = function(pVfs, nOut, pOut){ - const heap = wasm.heap8u(); - let i = 0; - for(; i < nOut; ++i) heap[pOut + i] = (Math.random()*255000) & 0xFF; - return i; - }; - } - if(!opfsVfs.$xSleep){ - /* If we can inherit an xSleep() impl from the default VFS then - use it, otherwise install one which is certainly less accurate - because it has to go round-trip through the async worker, but - provides the only option for a synchronous sleep() in JS. */ - vfsSyncWrappers.xSleep = (pVfs,ms)=>opRun('xSleep',ms); - } - - /* Install the vfs/io_methods into their C-level shared instances... */ - let inst = installMethod(opfsIoMethods); - for(let k of Object.keys(ioSyncWrappers)) inst(k, ioSyncWrappers[k]); - inst = installMethod(opfsVfs); - for(let k of Object.keys(vfsSyncWrappers)) inst(k, vfsSyncWrappers[k]); - - const sanityCheck = async function(){ - //state.ioBuf = new Uint8Array(state.sabIo); - const scope = wasm.scopedAllocPush(); - const sq3File = new sqlite3_file(); - try{ - const fid = sq3File.pointer; - const openFlags = capi.SQLITE_OPEN_CREATE - | capi.SQLITE_OPEN_READWRITE - //| capi.SQLITE_OPEN_DELETEONCLOSE - | capi.SQLITE_OPEN_MAIN_DB; - const pOut = wasm.scopedAlloc(8); - const dbFile = "/sanity/check/file"; - const zDbFile = wasm.scopedAllocCString(dbFile); - let rc; - vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); - rc = wasm.getMemValue(pOut,'i32'); - log("xAccess(",dbFile,") exists ?=",rc); - rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile, - fid, openFlags, pOut); - log("open rc =",rc,"state.opSABView[xOpen] =",state.opSABView[state.opIds.xOpen]); - if(isWorkerErrCode(rc)){ - error("open failed with code",rc); - return; - } - vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); - rc = wasm.getMemValue(pOut,'i32'); - if(!rc) toss("xAccess() failed to detect file."); - rc = ioSyncWrappers.xSync(sq3File.pointer, 0); - if(rc) toss('sync failed w/ rc',rc); - rc = ioSyncWrappers.xTruncate(sq3File.pointer, 1024); - if(rc) toss('truncate failed w/ rc',rc); - wasm.setMemValue(pOut,0,'i64'); - rc = ioSyncWrappers.xFileSize(sq3File.pointer, pOut); - if(rc) toss('xFileSize failed w/ rc',rc); - log("xFileSize says:",wasm.getMemValue(pOut, 'i64')); - rc = ioSyncWrappers.xWrite(sq3File.pointer, zDbFile, 10, 1); - if(rc) toss("xWrite() failed!"); - const readBuf = wasm.scopedAlloc(16); - rc = ioSyncWrappers.xRead(sq3File.pointer, readBuf, 6, 2); - wasm.setMemValue(readBuf+6,0); - let jRead = wasm.cstringToJs(readBuf); - log("xRead() got:",jRead); - if("sanity"!==jRead) toss("Unexpected xRead() value."); - log("xSleep()ing before close()ing..."); - opRun('xSleep',1000); - rc = ioSyncWrappers.xClose(fid); - log("xClose rc =",rc,"opSABView =",state.opSABView); - log("Deleting file:",dbFile); - vfsSyncWrappers.xDelete(opfsVfs.pointer, zDbFile, 0x1234); - vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); - rc = wasm.getMemValue(pOut,'i32'); - if(rc) toss("Expecting 0 from xAccess(",dbFile,") after xDelete()."); - }finally{ - sq3File.dispose(); - wasm.scopedAllocPop(scope); - } - }; - - W.onmessage = function({data}){ - //log("Worker.onmessage:",data); - switch(data.type){ - case 'loaded': - /*Pass our config and shared state on to the async worker.*/ - wMsg('init',state); - break; - case 'inited':{ - /*Indicates that the async partner has received the 'init', - so we now know that the state object is no longer subject to - being copied by a pending postMessage() call.*/ - try { - const rc = capi.sqlite3_vfs_register(opfsVfs.pointer, opfsVfs.$zName); - if(rc){ - opfsVfs.dispose(); - toss("sqlite3_vfs_register(OPFS) failed with rc",rc); - } - if(opfsVfs.pointer !== capi.sqlite3_vfs_find("opfs")){ - toss("BUG: sqlite3_vfs_find() failed for just-installed OPFS VFS"); - } - capi.sqlite3_vfs_register.addReference(opfsVfs, opfsIoMethods); - state.opSABView = new Int32Array(state.opSAB); - - if(thisUrl.searchParams.has('opfs-sanity-check')){ - warn("Running sanity checks because of opfs-sanity-check URL arg..."); - sanityCheck(); - } - warn("End of (very incomplete) OPFS setup.", opfsVfs); - }catch(e){ - error(e); - } - break; - } - default: - error("Unexpected message from the async worker:",data); - break; - } - }; -}/*initOpfsVfs*/ + const pVfs = capi.sqlite3_vfs_find("opfs") || toss("Unexpectedly missing 'opfs' VFS."); + const oVfs = capi.sqlite3_vfs.instanceForPointer(pVfs); + log("OPFS VFS:",pVfs, oVfs); + log("Done!"); +}/*tryOpfsVfs()*/; importScripts('sqlite3.js'); -self.sqlite3InitModule().then((EmscriptenModule)=>initOpfsVfs(EmscriptenModule.sqlite3)); +self.sqlite3InitModule().then((EmscriptenModule)=>{ + EmscriptenModule.sqlite3.installOpfsVfs() + .then((sqlite3)=>tryOpfsVfs(sqlite3)) + .catch((e)=>{ + console.error("Error initializing OPFS VFS:",e); + throw e; + }); +}); diff --git a/manifest b/manifest index 561605cbab..98c1a9df86 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Plug\sOPFS\smethods\sin\sto\stheir\ssqlite3_vfs/io_methods\scounterparts.\sAdd\sURL\sargs\sto\scontrol\sdebug\soutput\sand\srunning\sof\ssanity-checks\sin\sthe\sOPFS\sinit\scode. -D 2022-09-18T00:16:12.445 +C Move\sthe\sOPFS\sVFS\sbits\sback\sinto\sapi/sqlite3-api-opfs.js.\sRefactor\sthe\sOPFS\sVFS\sinit\sprocess\sto\suse\sa\sPromise-returning\sfunction\swhich\sthe\sclient\smust\scall,\sas\sthat\seliminates\sany\suncertainty\sabout\swhen\sthe\sVFS\s(necessarily\sactivated\sasynchronously)\sactually\sbecomes\savailable\sto\sthe\sclient. +D 2022-09-18T02:35:30.998 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 0323a7597383bf0dab473304f3a8a7e29d49298d92b5413692c012be2dfa84bf +F ext/wasm/GNUmakefile 24e5802cc186b492b3ef6290b64b857e51aa0afaa929b74a68f9fb457c4e8814 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 150a793a47205b8009ac934f3b6d6ebf67b965c072339aaa25ce808a19e116cc F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -484,7 +484,7 @@ F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 F ext/wasm/api/sqlite3-api-oo1.js d7526517f7ad3f6bda16ad66d373bbb71b43168deef7af60eda5c9fe873d1387 -F ext/wasm/api/sqlite3-api-opfs.js 130f60cc8f5835f9d77d4f12308bf4c8fb6d9c315009fc7239c5d67ff2bc8c67 +F ext/wasm/api/sqlite3-api-opfs.js 87d98f2449d5790efd7044e492166e4ed767e3320a03ed5a173b2b9364fc4896 F ext/wasm/api/sqlite3-api-prologue.js 48ebca4ae340b0242d4f39bbded01bd0588393c8023628be1c454b4db6f7bd6e F ext/wasm/api/sqlite3-api-worker1.js d33062afa045fd4be01ba4abc266801807472558b862b30056211b00c9c347b4 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 @@ -534,7 +534,7 @@ F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c2 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c F ext/wasm/wasmfs.make 21a5cf297954a689e0dc2a95299ae158f681cae5e90c10b99d986097815fd42d F ext/wasm/x-sync-async.html d85cb9b1ab398ef5a20ce64a689ce4bf9a347ffe69edd46c2d3dc57b869a8925 -F ext/wasm/x-sync-async.js 2cd04d73ddc515479cc2e4b9246d6da21b3f494020261d47470f4b710c84c0da +F ext/wasm/x-sync-async.js 95142516c0e467480fb87e71d7aab5b8ecee07fe7bd4babb5f5aa9b5b6ace4d0 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2030,8 +2030,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P cd06cc670029763955cf60ffcf944b36d41cb005b859d9b9fd0eea1b6741d0e9 -R af48df928e44c11df9f0c9eb2cf8376e +P a0e93ed20b2463606a63b03ce8ca41ec1fb22886db5c5c898ace86ba24636f70 +R 088ef11f22396ef93f0589b73a5e4797 U stephan -Z 07b2e0eb2359d701cc905c5402ed9c24 +Z 0f7cc3e7ea750e2836a1d35bc86649c9 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 274fb7f0c1..955e61f29c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a0e93ed20b2463606a63b03ce8ca41ec1fb22886db5c5c898ace86ba24636f70 \ No newline at end of file +1c660970d0f62bcfd6e698a72b050d99972a1e39f45a5ac24194a190f8f78ab3 \ No newline at end of file From 0db3089576b6df43fa477100446ab330b5bda905 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 18 Sep 2022 03:05:55 +0000 Subject: [PATCH 127/428] Move the OPFS VFS bits back into api/sqlite3-api-opfs.js. Refactor the OPFS VFS init process to use a Promise-returning function which the client must call, as that eliminates any uncertainty about when the VFS (necessarily activated asynchronously) actually becomes available to the client. Rename x-sync-async.* to test-opfs-vfs.* Milestone: first successful test of OPFS without WASMFS. FossilOrigin-Name: b2abf60dbfa6648f671a3932cb65feb28d05a0d5b7f792351d14f9c13d9798c5 --- ext/wasm/index.html | 11 +++-- ext/wasm/sqlite3-opfs-async-proxy.js | 5 ++- .../{x-sync-async.html => test-opfs-vfs.html} | 2 +- .../{x-sync-async.js => test-opfs-vfs.js} | 40 ++++++++++++++----- manifest | 18 ++++----- manifest.uuid | 2 +- 6 files changed, 50 insertions(+), 28 deletions(-) rename ext/wasm/{x-sync-async.html => test-opfs-vfs.html} (91%) rename ext/wasm/{x-sync-async.js => test-opfs-vfs.js} (59%) diff --git a/ext/wasm/index.html b/ext/wasm/index.html index 562bb9b5ef..fc0c33e432 100644 --- a/ext/wasm/index.html +++ b/ext/wasm/index.html @@ -50,11 +50,14 @@ worker due to an Emscripten limitation.
  • scratchpad-opfs-worker: experimenting with OPFS from a Worker thread (without WASMFS).
  • -
  • x-sync-async is an +
  • test-opfs-vfs + (same + with verbose output and sanity-checking tests) is an experiment in implementing a syncronous sqlite3 VFS proxy - for a fully synchronous backend interface (namely OPFS), using SharedArrayBuffer - and the Atomics APIs to regulate communication between the synchronous - interface and the async impl. + for a fully asynchronous backend interface (namely OPFS), + using SharedArrayBuffer and the Atomics APIs to regulate + communication between the synchronous interface and the + async impl.
  • diff --git a/ext/wasm/sqlite3-opfs-async-proxy.js b/ext/wasm/sqlite3-opfs-async-proxy.js index 58498e006b..00b4025569 100644 --- a/ext/wasm/sqlite3-opfs-async-proxy.js +++ b/ext/wasm/sqlite3-opfs-async-proxy.js @@ -260,7 +260,7 @@ const vfsAsyncImpls = { const fh = __openFiles[fid]; try{ const aRead = new Uint8Array(fh.sab, 0, n); - const nRead = fh.accessHandle.read(aRead, {at: offset}); + const nRead = fh.accessHandle.read(aRead, {at: Number(offset)}); if(nRead < n){/* Zero-fill remaining bytes */ new Uint8Array(fh.sab).fill(0, nRead, n); rc = state.sq3Codes.SQLITE_IOERR_SHORT_READ; @@ -302,7 +302,8 @@ const vfsAsyncImpls = { const fh = __openFiles[fid]; try{ affirmNotRO('xWrite', fh); - const nOut = fh.accessHandle.write(new Uint8Array(fh.sab, 0, n), {at: offset}); + const nOut = fh.accessHandle.write(new Uint8Array(fh.sab, 0, n), + {at: Number(offset)}); rc = (nOut===n) ? 0 : state.sq3Codes.SQLITE_IOERR_WRITE; }catch(e){ error("xWrite():",e,fh); diff --git a/ext/wasm/x-sync-async.html b/ext/wasm/test-opfs-vfs.html similarity index 91% rename from ext/wasm/x-sync-async.html rename to ext/wasm/test-opfs-vfs.html index b83d93378c..6e470ecc88 100644 --- a/ext/wasm/x-sync-async.html +++ b/ext/wasm/test-opfs-vfs.html @@ -17,6 +17,6 @@
    - + diff --git a/ext/wasm/x-sync-async.js b/ext/wasm/test-opfs-vfs.js similarity index 59% rename from ext/wasm/x-sync-async.js rename to ext/wasm/test-opfs-vfs.js index b25f5a2681..fd71c9cdcf 100644 --- a/ext/wasm/x-sync-async.js +++ b/ext/wasm/test-opfs-vfs.js @@ -25,20 +25,39 @@ const tryOpfsVfs = function(sqlite3){ const toss = function(...args){throw new Error(args.join(' '))}; const logPrefix = "OPFS tester:"; - const log = (...args)=>{ - console.log(logPrefix,...args); - }; - const warn = (...args)=>{ - console.warn(logPrefix,...args); - }; - const error = (...args)=>{ - console.error(logPrefix,...args); - }; + const log = (...args)=>console.log(logPrefix,...args); + const warn = (...args)=>console.warn(logPrefix,...args); + const error = (...args)=>console.error(logPrefix,...args); log("tryOpfsVfs()"); const capi = sqlite3.capi; const pVfs = capi.sqlite3_vfs_find("opfs") || toss("Unexpectedly missing 'opfs' VFS."); - const oVfs = capi.sqlite3_vfs.instanceForPointer(pVfs); + const oVfs = capi.sqlite3_vfs.instanceForPointer(pVfs) || toss("Unexpected instanceForPointer() result.");; log("OPFS VFS:",pVfs, oVfs); + + const dbFile = "my-persistent.db"; + const db = new sqlite3.oo1.DB(dbFile, "c", "opfs"); + log("db file:",db.filename); + try{ + let n = db.selectValue("select count(*) from sqlite_schema"); + if(n){ + log("Persistent data found. sqlite_schema entry count =",n); + } + db.transaction((db)=>{ + db.exec({ + sql:[ + "create table if not exists t(a);", + "insert into t(a) values(?),(?),(?);", + ], + bind: [performance.now() | 0, + (performance.now() |0) / 2, + (performance.now() |0) / 4] + }); + }); + log("count(*) from t =",db.selectValue("select count(*) from t")); + }finally{ + db.close(); + } + log("Done!"); }/*tryOpfsVfs()*/; @@ -48,6 +67,5 @@ self.sqlite3InitModule().then((EmscriptenModule)=>{ .then((sqlite3)=>tryOpfsVfs(sqlite3)) .catch((e)=>{ console.error("Error initializing OPFS VFS:",e); - throw e; }); }); diff --git a/manifest b/manifest index 98c1a9df86..ab3e8572f1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Move\sthe\sOPFS\sVFS\sbits\sback\sinto\sapi/sqlite3-api-opfs.js.\sRefactor\sthe\sOPFS\sVFS\sinit\sprocess\sto\suse\sa\sPromise-returning\sfunction\swhich\sthe\sclient\smust\scall,\sas\sthat\seliminates\sany\suncertainty\sabout\swhen\sthe\sVFS\s(necessarily\sactivated\sasynchronously)\sactually\sbecomes\savailable\sto\sthe\sclient. -D 2022-09-18T02:35:30.998 +C Move\sthe\sOPFS\sVFS\sbits\sback\sinto\sapi/sqlite3-api-opfs.js.\sRefactor\sthe\sOPFS\sVFS\sinit\sprocess\sto\suse\sa\sPromise-returning\sfunction\swhich\sthe\sclient\smust\scall,\sas\sthat\seliminates\sany\suncertainty\sabout\swhen\sthe\sVFS\s(necessarily\sactivated\sasynchronously)\sactually\sbecomes\savailable\sto\sthe\sclient.\sRename\sx-sync-async.*\sto\stest-opfs-vfs.*\sMilestone:\sfirst\ssuccessful\stest\sof\sOPFS\swithout\sWASMFS. +D 2022-09-18T03:05:55.278 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -502,7 +502,7 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 -F ext/wasm/index.html b8a47afa96d0c7ac3081c6d469e1fd7be6b87c64b94e4fb62e26984a1564cac4 +F ext/wasm/index.html 492eb6c9023c9cda391c61702a28bd18e6c986ca2588ec3f384275bb77275285 F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f @@ -523,9 +523,11 @@ F ext/wasm/speedtest1.html fbb8e4d1639028443f3687a683be660beca6927920545cf6b1fdf F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 -F ext/wasm/sqlite3-opfs-async-proxy.js ef012be0a77d3e34dc699de2e430b6d26a58c69dbebdd75248d8f41540996c71 +F ext/wasm/sqlite3-opfs-async-proxy.js 456bef1253fd4732f133b601a4450b7f8461e67af6e8d30bf8a239ad775c77a2 F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e +F ext/wasm/test-opfs-vfs.html 3e11c875c28f041891deeea6b2375121845ee1269cac6747df957ec0c7d4d37a w ext/wasm/x-sync-async.html +F ext/wasm/test-opfs-vfs.js bf70cd553a443b4eda63b577787ef73144f879fa062a20a73bb44e3242c81a15 w ext/wasm/x-sync-async.js F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 F ext/wasm/testing-worker1-promiser.js 63448fddfd3b8c89ff667d17c8b31c6c2259dd4647ebbbd28f3a921c48e924da F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae @@ -533,8 +535,6 @@ F ext/wasm/testing1.js 7cd8ab255c238b030d928755ae8e91e7d90a12f2ae601b1b8f7827aaa F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c F ext/wasm/wasmfs.make 21a5cf297954a689e0dc2a95299ae158f681cae5e90c10b99d986097815fd42d -F ext/wasm/x-sync-async.html d85cb9b1ab398ef5a20ce64a689ce4bf9a347ffe69edd46c2d3dc57b869a8925 -F ext/wasm/x-sync-async.js 95142516c0e467480fb87e71d7aab5b8ecee07fe7bd4babb5f5aa9b5b6ace4d0 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2030,8 +2030,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P a0e93ed20b2463606a63b03ce8ca41ec1fb22886db5c5c898ace86ba24636f70 -R 088ef11f22396ef93f0589b73a5e4797 +P 1c660970d0f62bcfd6e698a72b050d99972a1e39f45a5ac24194a190f8f78ab3 +R 039a2cd7ec4da87358ad6184da91bc68 U stephan -Z 0f7cc3e7ea750e2836a1d35bc86649c9 +Z 58e9ce410c47c756bc153984b857c4cf # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 955e61f29c..d80c68d21c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1c660970d0f62bcfd6e698a72b050d99972a1e39f45a5ac24194a190f8f78ab3 \ No newline at end of file +b2abf60dbfa6648f671a3932cb65feb28d05a0d5b7f792351d14f9c13d9798c5 \ No newline at end of file From f38601206997aa909b2cd6dba02cb0b4e13e8e2a Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 18 Sep 2022 17:32:35 +0000 Subject: [PATCH 128/428] Numerous cleanups in the JS bits. Removed some now-defunct wasm test files. Expose sqlite3.opfs object containing various OPFS-specific utilities. FossilOrigin-Name: 26e625d05d9820033b23536f18ad3ddc59ed712ad507d4b0c7fe88abd15d2be8 --- ext/wasm/api/sqlite3-api-oo1.js | 131 +++++-- ext/wasm/api/sqlite3-api-opfs.js | 128 +++++-- ext/wasm/api/sqlite3-api-prologue.js | 87 +++-- ext/wasm/api/sqlite3-api-worker1.js | 6 +- ext/wasm/index.html | 2 - ext/wasm/scratchpad-opfs-worker.html | 18 - ext/wasm/scratchpad-opfs-worker.js | 47 --- ext/wasm/scratchpad-opfs-worker2.js | 494 --------------------------- ext/wasm/scratchpad-wasmfs-main.js | 2 +- ext/wasm/sqlite3-opfs-async-proxy.js | 22 +- ext/wasm/test-opfs-vfs.html | 4 +- ext/wasm/test-opfs-vfs.js | 34 +- ext/wasm/testing1.js | 6 +- manifest | 33 +- manifest.uuid | 2 +- 15 files changed, 337 insertions(+), 679 deletions(-) delete mode 100644 ext/wasm/scratchpad-opfs-worker.html delete mode 100644 ext/wasm/scratchpad-opfs-worker.js delete mode 100644 ext/wasm/scratchpad-opfs-worker2.js diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 368986933f..fb01e98713 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -85,9 +85,24 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ /** A proxy for DB class constructors. It must be called with the - being-construct DB object as its "this". + being-construct DB object as its "this". See the DB constructor + for the argument docs. This is split into a separate function + in order to enable simple creation of special-case DB constructors, + e.g. a hypothetical LocalStorageDB or OpfsDB. + + Expects to be passed a configuration object with the following + properties: + + - `.filename`: the db filename. It may be a special name like ":memory:" + or "". + + - `.flags`: as documented in the DB constructor. + + - `.vfs`: as documented in the DB constructor. + + It also accepts those as the first 3 arguments. */ - const dbCtorHelper = function ctor(fn=':memory:', flags='c', vfsName){ + const dbCtorHelper = function ctor(...args){ if(!ctor._name2vfs){ // Map special filenames which we handle here (instead of in C) // to some helpful metadata... @@ -104,25 +119,33 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ filename: isWorkerThread || (()=>'session') }; } - if('string'!==typeof fn){ - toss3("Invalid filename for DB constructor."); + const opt = ctor.normalizeArgs(...args); + let fn = opt.filename, vfsName = opt.vfs, flagsStr = opt.flags; + if(('string'!==typeof fn && 'number'!==typeof fn) + || 'string'!==typeof flagsStr + || (vfsName && ('string'!==typeof vfsName && 'number'!==typeof vfsName))){ + console.error("Invalid DB ctor args",opt,arguments); + toss3("Invalid arguments for DB constructor."); } - const vfsCheck = ctor._name2vfs[fn]; + let fnJs = ('number'===typeof fn) ? capi.wasm.cstringToJs(fn) : fn; + const vfsCheck = ctor._name2vfs[fnJs]; if(vfsCheck){ vfsName = vfsCheck.vfs; - fn = vfsCheck.filename(fn); + fn = fnJs = vfsCheck.filename(fnJs); } let ptr, oflags = 0; - if( flags.indexOf('c')>=0 ){ + if( flagsStr.indexOf('c')>=0 ){ oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; } - if( flags.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE; + if( flagsStr.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE; if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY; oflags |= capi.SQLITE_OPEN_EXRESCODE; const stack = capi.wasm.scopedAllocPush(); try { const ppDb = capi.wasm.scopedAllocPtr() /* output (sqlite3**) arg */; - const pVfsName = vfsName ? capi.wasm.scopedAllocCString(vfsName) : 0; + const pVfsName = vfsName ? ( + ('number'===typeof vfsName ? vfsName : capi.wasm.scopedAllocCString(vfsName)) + ): 0; const rc = capi.sqlite3_open_v2(fn, ppDb, oflags, pVfsName); ptr = capi.wasm.getPtrValue(ppDb); checkSqlite3Rc(ptr, rc); @@ -132,11 +155,36 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }finally{ capi.wasm.scopedAllocPop(stack); } - this.filename = fn; + this.filename = fnJs; __ptrMap.set(this, ptr); __stmtMap.set(this, Object.create(null)); __udfMap.set(this, Object.create(null)); }; + + /** + A helper for DB constructors. It accepts either a single + config-style object or up to 3 arguments (filename, dbOpenFlags, + dbVfsName). It returns a new object containing: + + { filename: ..., flags: ..., vfs: ... } + + If passed an object, any additional properties it has are copied + as-is into the new object. + */ + dbCtorHelper.normalizeArgs = function(filename,flags = 'c',vfs = null){ + const arg = {}; + if(1===arguments.length && 'object'===typeof arguments[0]){ + const x = arguments[0]; + Object.keys(x).forEach((k)=>arg[k] = x[k]); + if(undefined===arg.flags) arg.flags = 'c'; + if(undefined===arg.vfs) arg.vfs = null; + }else{ + arg.filename = filename; + arg.flags = flags; + arg.vfs = vfs; + } + return arg; + }; /** The DB class provides a high-level OO wrapper around an sqlite3 @@ -175,6 +223,17 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ or not at all, to use the default. If passed a value, it must be the string name of a VFS + The constructor optionally (and preferably) takes its arguments + in the form of a single configuration object with the following + properties: + + - `.filename`: database file name + - `.flags`: open-mode flags + - `.vfs`: the VFS fname + + The `filename` and `vfs` arguments may be either JS strings or + C-strings allocated via WASM. + For purposes of passing a DB instance to C-style sqlite3 functions, the DB object's read-only `pointer` property holds its `sqlite3*` pointer value. That property can also be used to check @@ -187,12 +246,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ the database. In this mode, only a single database is permitted in each storage object. This feature is experimental and subject to any number of changes (including outright removal). This - support requires a specific build of sqlite3, the existence of - which can be determined at runtime by checking for a non-0 return - value from sqlite3.capi.sqlite3_vfs_find("kvvfs"). + support requires the kvvfs sqlite3 VFS, the existence of which + can be determined at runtime by checking for a non-0 return value + from sqlite3.capi.sqlite3_vfs_find("kvvfs"). */ - const DB = function ctor(fn=':memory:', flags='c', vfsName){ - dbCtorHelper.apply(this, Array.prototype.slice.call(arguments)); + const DB = function(...args){ + dbCtorHelper.apply(this, args); }; /** @@ -361,12 +420,31 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ closed. After calling close(), `this.pointer` will resolve to `undefined`, so that can be used to check whether the db instance is still opened. + + If this.onclose.before is a function then it is called before + any close-related cleanup. + + If this.onclose.after is a function then it is called after the + db is closed but before auxiliary state like this.filename is + cleared. + + Both onclose handlers are passed this object. If this db is not + opened, neither of the handlers are called. Any exceptions the + handlers throw are ignored because "destructors must not + throw." + + Note that garbage collection of a db handle, if it happens at + all, will never trigger close(), so onclose handlers are not a + reliable way to implement close-time cleanup or maintenance of + a db. */ close: function(){ if(this.pointer){ + if(this.onclose && (this.onclose.before instanceof Function)){ + try{this.onclose.before(this)} + catch(e){/*ignore*/} + } const pDb = this.pointer; - let s; - const that = this; Object.keys(__stmtMap.get(this)).forEach((k,s)=>{ if(s && s.pointer) s.finalize(); }); @@ -377,6 +455,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ __stmtMap.delete(this); __udfMap.delete(this); capi.sqlite3_close_v2(pDb); + if(this.onclose && (this.onclose.after instanceof Function)){ + try{this.onclose.after(this)} + catch(e){/*ignore*/} + } delete this.filename; } }, @@ -401,13 +483,13 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } }, /** - 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`. + Similar to this.filename but will return a falsy value for + special names like ":memory:". 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='main'){ + getFilename: function(dbName='main'){ return capi.sqlite3_db_filename(affirmDbOpen(this).pointer, dbName); }, /** @@ -1591,7 +1673,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ooApi: "0.1" }, DB, - Stmt + Stmt, + dbCtorHelper }/*oo1 object*/; }); diff --git a/ext/wasm/api/sqlite3-api-opfs.js b/ext/wasm/api/sqlite3-api-opfs.js index af1c6f7608..ede4fb32b1 100644 --- a/ext/wasm/api/sqlite3-api-opfs.js +++ b/ext/wasm/api/sqlite3-api-opfs.js @@ -42,8 +42,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ that number will increase as the OPFS API matures). - The OPFS features used here are only available in dedicated Worker - threads. This file tries to detect that case and becomes a no-op - if those features do not seem to be available. + threads. This file tries to detect that case, resulting in a + rejected Promise if those features do not seem to be available. - It requires the SharedArrayBuffer and Atomics classes, and the former is only available if the HTTP server emits the so-called @@ -72,7 +72,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ returned Promise resolves. On success, the Promise resolves to the top-most sqlite3 namespace - object. + object and that object gets a new object installed in its + `opfs` property, containing several OPFS-specific utilities. */ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri){ const options = (asyncProxyUri && 'object'===asyncProxyUri) ? asyncProxyUri : { @@ -89,6 +90,13 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) options.proxyUri = callee.defaultProxyUri; } delete sqlite3.installOpfsVfs; + + /** + Generic utilities for working with OPFS. This will get filled out + by the Promise setup and, on success, installed as sqlite3.opfs. + */ + const opfsUtil = Object.create(null); + const thePromise = new Promise(function(promiseResolve, promiseReject){ const logPrefix = "OPFS syncer:"; const warn = (...args)=>{ @@ -118,9 +126,8 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) const sqlite3_vfs = capi.sqlite3_vfs; const sqlite3_file = capi.sqlite3_file; const sqlite3_io_methods = capi.sqlite3_io_methods; - const StructBinder = sqlite3.StructBinder; const W = new Worker(options.proxyUri); - const workerOrigOnError = W.onrror; + W._originalOnError = W.onerror /* will be restored later */; W.onerror = function(err){ promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons.")); }; @@ -131,17 +138,37 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) This object must initially contain only cloneable or sharable objects. After the worker's "inited" message arrives, other types of data may be added to it. + + For purposes of Atomics.wait() and Atomics.notify(), we use a + SharedArrayBuffer with one slot reserved for each of the API + proxy's methods. The sync side of the API uses Atomics.wait() + on the corresponding slot and the async side uses + Atomics.notify() on that slot. + + The approach of using a single SAB to serialize comms for all + instances might(?) lead to deadlock situations in multi-db + cases. We should probably have one SAB here with a single slot + for locking a per-file initialization step and then allocate a + separate SAB like the above one for each file. That will + require a bit of acrobatics but should be feasible. */ const state = Object.create(null); state.verbose = options.verbose; - state.fileBufferSize = 1024 * 64 + 8 /* size of fileHandle.sab. 64k = max sqlite3 page size */; - state.fbInt64Offset = state.fileBufferSize - 8 /*spot in fileHandle.sab to store an int64*/; + state.fileBufferSize = + 1024 * 64 + 8 /* size of aFileHandle.sab. 64k = max sqlite3 page + size. The additional bytes are space for + holding BigInt results, since we cannot store + those via the Atomics API (which only works on + an Int32Array). */; + state.fbInt64Offset = + state.fileBufferSize - 8 /*spot in fileHandle.sab to store an int64 result */; state.opIds = Object.create(null); { let i = 0; state.opIds.xAccess = i++; state.opIds.xClose = i++; state.opIds.xDelete = i++; + state.opIds.xDeleteNoWait = i++; state.opIds.xFileSize = i++; state.opIds.xOpen = i++; state.opIds.xRead = i++; @@ -149,14 +176,8 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) state.opIds.xSync = i++; state.opIds.xTruncate = i++; state.opIds.xWrite = i++; + state.opIds.mkdir = i++; state.opSAB = new SharedArrayBuffer(i * 4/*sizeof int32*/); - /* The approach of using a single SAB to serialize comms for all - instances may(?) lead to deadlock situations in multi-db - cases. We should probably have one SAB here with a single slot - for locking a per-file initialization step and then allocate a - separate SAB like the above one for each file. That will - require a bit of acrobatics but should be feasible. - */ } state.sq3Codes = Object.create(null); @@ -167,15 +188,14 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) 'SQLITE_IOERR_READ', 'SQLITE_IOERR_SHORT_READ', 'SQLITE_IOERR_WRITE', 'SQLITE_IOERR_FSYNC', 'SQLITE_IOERR_TRUNCATE', 'SQLITE_IOERR_DELETE', - 'SQLITE_IOERR_ACCESS', 'SQLITE_IOERR_CLOSE' + 'SQLITE_IOERR_ACCESS', 'SQLITE_IOERR_CLOSE', + 'SQLITE_IOERR_DELETE' ].forEach(function(k){ state.sq3Codes[k] = capi[k] || toss("Maintenance required: not found:",k); state.sq3Codes._reverse[capi[k]] = k; }); const isWorkerErrCode = (n)=>!!state.sq3Codes._reverse[n]; - const opStore = (op,val=-1)=>Atomics.store(state.opSABView, state.opIds[op], val); - const opWait = (op,val=-1)=>Atomics.wait(state.opSABView, state.opIds[op], val); /** Runs the given operation in the async worker counterpart, waits @@ -185,9 +205,9 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) given operation's signature in the async API counterpart. */ const opRun = (op,args)=>{ - opStore(op); + Atomics.store(state.opSABView, state.opIds[op], -1); wMsg(op, args); - opWait(op); + Atomics.wait(state.opSABView, state.opIds[op], -1); return Atomics.load(state.opSABView, state.opIds[op]); }; @@ -268,7 +288,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) func with the same signature as described above. */ const installMethod = function callee(tgt, name, func){ - if(!(tgt instanceof StructBinder.StructType)){ + if(!(tgt instanceof sqlite3.StructBinder.StructType)){ toss("Usage error: target object is-not-a StructType."); } if(1===arguments.length){ @@ -429,7 +449,10 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) return 0; }, xDelete: function(pVfs, zName, doSyncDir){ - return opRun('xDelete', {filename: wasm.cstringToJs(zName), syncDir: doSyncDir}); + opRun('xDelete', {filename: wasm.cstringToJs(zName), syncDir: doSyncDir}); + /* We're ignoring errors because we cannot yet differentiate + between harmless and non-harmless failures. */ + return 0; }, xFullPathname: function(pVfs,zName,nOut,pOut){ /* Until/unless we have some notion of "current dir" @@ -521,6 +544,65 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) for(let k of Object.keys(ioSyncWrappers)) inst(k, ioSyncWrappers[k]); inst = installMethod(opfsVfs); for(let k of Object.keys(vfsSyncWrappers)) inst(k, vfsSyncWrappers[k]); + + + /** + Syncronously deletes the given OPFS filesystem entry, ignoring + any errors. As this environment has no notion of "current + directory", the given name must be an absolute path. If the 2nd + argument is truthy, deletion is recursive (use with caution!). + + Returns true if the deletion succeeded and fails if it fails, + but cannot report the nature of the failure. + */ + opfsUtil.deleteEntry = function(fsEntryName,recursive){ + return 0===opRun('xDelete', {filename:fsEntryName, recursive}); + }; + /** + Exactly like deleteEntry() but runs asynchronously. + */ + opfsUtil.deleteEntryAsync = async function(fsEntryName,recursive){ + wMsg('xDeleteNoWait', {filename: fsEntryName, recursive}); + }; + /** + Synchronously creates the given directory name, recursively, in + the OPFS filesystem. Returns true if it succeeds or the + directory already exists, else false. + */ + opfsUtil.mkdir = async function(absDirName){ + return 0===opRun('mkdir', absDirName); + }; + /** + Synchronously checks whether the given OPFS filesystem exists, + returning true if it does, false if it doesn't. + */ + opfsUtil.entryExists = function(fsEntryName){ + return 0===opRun('xAccess', fsEntryName); + }; + + /** + Generates a random ASCII string, intended for use as a + temporary file name. Its argument is the length of the string, + defaulting to 16. + */ + opfsUtil.randomFilename = randomFilename; + + if(sqlite3.oo1){ + opfsUtil.OpfsDb = function(...args){ + const opt = sqlite3.oo1.dbCtorHelper.normalizeArgs(...args); + opt.vfs = opfsVfs.$zName; + sqlite3.oo1.dbCtorHelper.call(this, opt); + }; + opfsUtil.OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype); + } + + /** + Potential TODOs: + + - Expose one or both of the Worker objects via opfsUtil and + publish an interface for proxying the higher-level OPFS + features like getting a directory listing. + */ const sanityCheck = async function(){ const scope = wasm.scopedAllocPush(); @@ -605,7 +687,9 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) warn("Running sanity checks because of opfs-sanity-check URL arg..."); sanityCheck(); } - W.onerror = workerOrigOnError; + W.onerror = W._originalOnError; + delete W._originalOnError; + sqlite3.opfs = opfsUtil; promiseResolve(sqlite3); log("End of OPFS sqlite3_vfs setup.", opfsVfs); }catch(e){ diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 1fc533da62..add8ad658e 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -689,7 +689,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( /** State for sqlite3_web_persistent_dir(). */ let __persistentDir; /** - An experiment. Do not use. + An experiment. Do not use in client code. If the wasm environment has a persistent storage directory, its path is returned by this function. If it does not then @@ -699,14 +699,18 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( environment to determine whether persistence filesystem support is available and, if it is, enables it (if needed). + This function currently only recognizes the WASMFS/OPFS storage + combination. "Plain" OPFS is provided via a separate VFS which + can optionally be installed (if OPFS is available on the system) + using sqlite3.installOpfsVfs(). + TODOs and caveats: - If persistent storage is available at the root of the virtual filesystem, this interface cannot currently distinguish that - from the lack of persistence. That case cannot currently (with - WASMFS/OPFS) happen, but it is conceivably possible in future - environments or non-browser runtimes (none of which are yet - supported targets). + from the lack of persistence. That can (in the mean time) + happen when using the JS-native "opfs" VFS, as opposed to the + WASMFS/OPFS combination. */ capi.sqlite3_web_persistent_dir = function(){ if(undefined !== __persistentDir) return __persistentDir; @@ -764,6 +768,49 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( capi.wasm.exports.sqlite3_initialize(); } + /** + Given an `sqlite3*` and an sqlite3_vfs name, returns a truthy + value (see below) if that db handle uses that VFS, else returns + false. If pDb is falsy then this function returns a truthy value + if the default VFS is that VFS. Results are undefined if pDb is + truthy but refers to an invalid pointer. + + The 2nd argument may either be a JS string or a C-string + allocated from the wasm environment. + + The truthy value it returns is a pointer to the `sqlite3_vfs` + object. + + To permit safe use of this function from APIs which may be called + via the C stack (like SQL UDFs), this function does not throw: if + bad arguments cause a conversion error when passing into + wasm-space, false is returned. + */ + capi.sqlite3_web_db_uses_vfs = function(pDb,vfsName){ + try{ + const pK = ('number'===vfsName) + ? capi.wasm.exports.sqlite3_vfs_find(vfsName) + : capi.sqlite3_vfs_find(vfsName); + if(!pK) return false; + else if(!pDb){ + return capi.sqlite3_vfs_find(0)===pK ? pK : false; + } + const ppVfs = capi.wasm.allocPtr(); + try{ + return ( + (0===capi.sqlite3_file_control( + pDb, "main", capi.SQLITE_FCNTL_VFS_POINTER, ppVfs + )) && (capi.wasm.getPtrValue(ppVfs) === pK) + ) ? pK : false; + }finally{ + capi.wasm.dealloc(ppVfs); + } + }catch(e){ + /* Ignore - probably bad args to a wasm-bound function. */ + return false; + } + }; + if( self.window===self ){ /* Features specific to the main window thread... */ @@ -812,7 +859,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( /** This routine guesses the approximate amount of window.localStorage and/or window.sessionStorage in use by the - kvvfs database backend. Its argument must be one of + kvvfs database backend. Its argument must be one of ('session', 'local', ''). In the first two cases, only sessionStorage resp. localStorage is counted. If it's an empty string (the default) then both are counted. Only storage keys @@ -842,34 +889,6 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( return sz * 2 /* because JS uses UC16 encoding */; }; - /** - Given an `sqlite3*`, returns a truthy value (see below) if that - db handle uses the "kvvfs" VFS, else returns false. If pDb is - NULL then this function returns true if the default VFS is - "kvvfs". Results are undefined if pDb is truthy but refers to - an invalid pointer. - - The truthy value it returns is a pointer to the kvvfs - `sqlite3_vfs` object. - */ - capi.sqlite3_web_db_is_kvvfs = function(pDb){ - const pK = capi.sqlite3_vfs_find("kvvfs"); - if(!pK) return false; - else if(!pDb){ - return capi.sqlite3_vfs_find(0) && pK; - } - const scope = capi.wasm.scopedAllocPush(); - try{ - const ppVfs = capi.wasm.scopedAllocPtr(); - return ( - (0===capi.sqlite3_file_control( - pDb, "main", capi.SQLITE_FCNTL_VFS_POINTER, ppVfs - )) && (capi.wasm.getPtrValue(ppVfs) === pK) - ) ? pK : false; - }finally{ - capi.wasm.scopedAllocPop(scope); - } - }; }/* main-window-only bits */ /* The remainder of the API will be set up in later steps. */ diff --git a/ext/wasm/api/sqlite3-api-worker1.js b/ext/wasm/api/sqlite3-api-worker1.js index 00359413b2..97f2677e6c 100644 --- a/ext/wasm/api/sqlite3-api-worker1.js +++ b/ext/wasm/api/sqlite3-api-worker1.js @@ -371,10 +371,14 @@ sqlite3.initWorker1API = function(){ close: function(db,alsoUnlink){ if(db){ delete this.dbs[getDbId(db)]; - const filename = db.fileName(); + const filename = db.getFilename(); db.close(); if(db===this.defaultDb) this.defaultDb = undefined; if(alsoUnlink && filename){ + /* This isn't necessarily correct: the db might be using a + VFS other than the default. How do we best resolve this + without having to special-case the kvvfs and opfs + VFSes? */ sqlite3.capi.wasm.sqlite3_wasm_vfs_unlink(filename); } } diff --git a/ext/wasm/index.html b/ext/wasm/index.html index fc0c33e432..d13f9bb7f3 100644 --- a/ext/wasm/index.html +++ b/ext/wasm/index.html @@ -48,8 +48,6 @@ experimenting with WASMFS/OPFS-based persistence. Maintenance reminder: we cannot currently (2022-09-15) load WASMFS in a worker due to an Emscripten limitation. -
  • scratchpad-opfs-worker: - experimenting with OPFS from a Worker thread (without WASMFS).
  • test-opfs-vfs (same with verbose output and sanity-checking tests) is an diff --git a/ext/wasm/scratchpad-opfs-worker.html b/ext/wasm/scratchpad-opfs-worker.html deleted file mode 100644 index 0ab7822d82..0000000000 --- a/ext/wasm/scratchpad-opfs-worker.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - sqlite3 OPFS Worker-thread Scratchpad - - -
    sqlite3 OPFS Worker-thread Scratchpad
    -

    All stuff on this page happens in the dev console.

    -
    -
    - - - diff --git a/ext/wasm/scratchpad-opfs-worker.js b/ext/wasm/scratchpad-opfs-worker.js deleted file mode 100644 index e27164775c..0000000000 --- a/ext/wasm/scratchpad-opfs-worker.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - 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. This file must be run in - main JS thread. It will load sqlite3.js in a worker thread. -*/ -'use strict'; -(function(){ - const toss = function(...args){throw new Error(args.join(' '))}; - const eOutput = document.querySelector('#test-output'); - const logHtml = function(cssClass,...args){ - if(Array.isArray(args[0])) args = args[0]; - const ln = document.createElement('div'); - if(cssClass) ln.classList.add(cssClass); - ln.append(document.createTextNode(args.join(' '))); - eOutput.append(ln); - }; - const log = function(...args){ - logHtml('',...args); - }; - const error = function(...args){ - logHtml('error',...args); - }; - const warn = function(...args){ - logHtml('warning',...args); - }; - - const W = new Worker("scratchpad-opfs-worker2.js"); - W.onmessage = function(ev){ - ev = ev.data; - const d = ev.data; - switch(ev.type){ - case 'stdout': log(d); break; - case 'stderr': error(d); break; - default: warn("Unhandled message type:",ev); break; - } - }; -})(); diff --git a/ext/wasm/scratchpad-opfs-worker2.js b/ext/wasm/scratchpad-opfs-worker2.js deleted file mode 100644 index 47ace63de4..0000000000 --- a/ext/wasm/scratchpad-opfs-worker2.js +++ /dev/null @@ -1,494 +0,0 @@ -/* - 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. - - *********************************************************************** - - An experiment for wasmfs/opfs. This file MUST be in the same dir as - the sqlite3.js emscripten module or that module won't be able to - resolve the relative URIs (importScript()'s relative URI handling - is, quite frankly, broken). -*/ -'use strict'; - -const toss = function(...args){throw new Error(args.join(' '))}; -/** - 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(...args){ - wMsg('stdout',args); - console.log(...args); -}; -const stderr = function(...args){ - wMsg('stderr',args); - console.error(...args); -}; - -const log = console.log.bind(console); -const warn = console.warn.bind(console); -const error = console.error.bind(console); - - -const initOpfsBits = async function(sqlite3){ - if(!self.importScripts || !self.FileSystemFileHandle){ - //|| !self.FileSystemFileHandle.prototype.createSyncAccessHandle){ - // ^^^ sync API is not required with WASMFS/OPFS backend. - warn("OPFS is not available in this environment."); - return; - }else if(!sqlite3.capi.wasm.bigIntEnabled){ - error("OPFS requires BigInt support but sqlite3.capi.wasm.bigIntEnabled is false."); - return; - } - //warn('self.FileSystemFileHandle =',self.FileSystemFileHandle); - //warn('self.FileSystemFileHandle.prototype =',self.FileSystemFileHandle.prototype); - const capi = sqlite3.capi, - wasm = capi.wasm; - const sqlite3_vfs = capi.sqlite3_vfs - || toss("Missing sqlite3.capi.sqlite3_vfs object."); - const sqlite3_file = capi.sqlite3_file - || toss("Missing sqlite3.capi.sqlite3_file object."); - const sqlite3_io_methods = capi.sqlite3_io_methods - || toss("Missing sqlite3.capi.sqlite3_io_methods object."); - const StructBinder = sqlite3.StructBinder || toss("Missing sqlite3.StructBinder."); - const debug = console.debug.bind(console), - log = console.log.bind(console); - warn("UNDER CONSTRUCTION: setting up OPFS VFS..."); - - const pDVfs = capi.sqlite3_vfs_find(null)/*pointer to default VFS*/; - const dVfs = pDVfs - ? new sqlite3_vfs(pDVfs) - : null /* dVfs will be null when sqlite3 is built with - SQLITE_OS_OTHER. Though we cannot currently handle - that case, the hope is to eventually be able to. */; - const oVfs = new sqlite3_vfs(); - const oIom = new sqlite3_io_methods(); - oVfs.$iVersion = 2/*yes, two*/; - oVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof; - oVfs.$mxPathname = 1024/*sure, why not?*/; - oVfs.$zName = wasm.allocCString("opfs"); - oVfs.ondispose = [ - '$zName', oVfs.$zName, - 'cleanup dVfs', ()=>(dVfs ? dVfs.dispose() : null) - ]; - if(dVfs){ - oVfs.$xSleep = dVfs.$xSleep; - oVfs.$xRandomness = dVfs.$xRandomness; - } - // All C-side memory of oVfs is zeroed out, but just to be explicit: - oVfs.$xDlOpen = oVfs.$xDlError = oVfs.$xDlSym = oVfs.$xDlClose = null; - - /** - Pedantic sidebar about oVfs.ondispose: the entries in that array - are items to clean up when oVfs.dispose() is called, but in this - environment it will never be called. The VFS instance simply - hangs around until the WASM module instance is cleaned up. We - "could" _hypothetically_ clean it up by "importing" an - sqlite3_os_end() impl into the wasm build, but the shutdown order - of the wasm engine and the JS one are undefined so there is no - guaranty that the oVfs instance would be available in one - environment or the other when sqlite3_os_end() is called (_if_ it - gets called at all in a wasm build, which is undefined). - */ - - /** - Installs a StructBinder-bound function pointer member of the - given name and function in the given StructType target object. - It creates a WASM proxy for the given function and arranges for - that proxy to be cleaned up when tgt.dispose() is called. Throws - on the slightest hint of error (e.g. tgt is-not-a StructType, - name does not map to a struct-bound member, etc.). - - Returns a proxy for this function which is bound to tgt and takes - 2 args (name,func). That function returns the same thing, - permitting calls to be chained. - - If called with only 1 arg, it has no side effects but returns a - func with the same signature as described above. - */ - const installMethod = function callee(tgt, name, func){ - if(!(tgt instanceof StructBinder.StructType)){ - toss("Usage error: target object is-not-a StructType."); - } - if(1===arguments.length){ - return (n,f)=>callee(tgt,n,f); - } - if(!callee.argcProxy){ - callee.argcProxy = function(func,sig){ - return function(...args){ - if(func.length!==arguments.length){ - toss("Argument mismatch. Native signature is:",sig); - } - return func.apply(this, args); - } - }; - callee.removeFuncList = function(){ - if(this.ondispose.__removeFuncList){ - this.ondispose.__removeFuncList.forEach( - (v,ndx)=>{ - if('number'===typeof v){ - try{wasm.uninstallFunction(v)} - catch(e){/*ignore*/} - } - /* else it's a descriptive label for the next number in - the list. */ - } - ); - delete this.ondispose.__removeFuncList; - } - }; - }/*static init*/ - const sigN = tgt.memberSignature(name); - if(sigN.length<2){ - toss("Member",name," is not a function pointer. Signature =",sigN); - } - const memKey = tgt.memberKey(name); - //log("installMethod",tgt, name, sigN); - const fProxy = 1 - // We can remove this proxy middle-man once the VFS is working - ? callee.argcProxy(func, sigN) - : func; - const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true)); - tgt[memKey] = pFunc; - if(!tgt.ondispose) tgt.ondispose = []; - if(!tgt.ondispose.__removeFuncList){ - tgt.ondispose.push('ondispose.__removeFuncList handler', - callee.removeFuncList); - tgt.ondispose.__removeFuncList = []; - } - tgt.ondispose.__removeFuncList.push(memKey, pFunc); - return (n,f)=>callee(tgt, n, f); - }/*installMethod*/; - - /** - Map of sqlite3_file pointers to OPFS handles. - */ - const __opfsHandles = Object.create(null); - - /** - Generates a random ASCII string len characters long, intended for - use as a temporary file name. - */ - const randomFilename = function f(len=16){ - if(!f._chars){ - f._chars = "abcdefghijklmnopqrstuvwxyz"+ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ - "012346789"; - f._n = f._chars.length; - } - const a = []; - let i = 0; - for( ; i < len; ++i){ - const ndx = Math.random() * (f._n * 64) % f._n | 0; - a[i] = f._chars[ndx]; - } - return a.join(''); - }; - - const rootDir = await navigator.storage.getDirectory(); - log("rootDir =",rootDir); - - //////////////////////////////////////////////////////////////////////// - // Set up OPFS VFS methods... - let inst = installMethod(oVfs); - inst('xOpen', function(pVfs, zName, pFile, flags, pOutFlags){ - const f = new sqlite3_file(pFile); - f.$pMethods = oIom.pointer; - __opfsHandles[pFile] = f; - f.opfsHandle = null /* TODO */; - if(flags & capi.SQLITE_OPEN_DELETEONCLOSE){ - f.deleteOnClose = true; - } - f.filename = zName ? wasm.cstringToJs(zName) : 'sqlite3-xOpen-'+randomFilename(); - error("OPFS sqlite3_vfs::xOpen is not yet full implemented."); - return capi.SQLITE_IOERR; - }) - ('xFullPathname', function(pVfs,zName,nOut,pOut){ - /* Until/unless we have some notion of "current dir" - in OPFS, simply copy zName to pOut... */ - const i = wasm.cstrncpy(pOut, zName, nOut); - return i remove() - return capi.SQLITE_IOERR; - }) - ('xGetLastError', function(pVfs,nOut,pOut){ - debug("OPFS sqlite3_vfs::xGetLastError() has nothing sensible to return."); - return 0; - }) - ('xCurrentTime', function(pVfs,pOut){ - /* If it turns out that we need to adjust for timezone, see: - https://stackoverflow.com/a/11760121/1458521 */ - wasm.setMemValue(pOut, 2440587.5 + (new Date().getTime()/86400000), - 'double'); - return 0; - }) - ('xCurrentTimeInt64',function(pVfs,pOut){ - // TODO: confirm that this calculation is correct - wasm.setMemValue(pOut, (2440587.5 * 86400000) + new Date().getTime(), - 'i64'); - return 0; - }); - if(!oVfs.$xSleep){ - inst('xSleep', function(pVfs,ms){ - error("sqlite3_vfs::xSleep(",ms,") cannot be implemented from "+ - "JS and we have no default VFS to copy the impl from."); - return 0; - }); - } - if(!oVfs.$xRandomness){ - inst('xRandomness', function(pVfs, nOut, pOut){ - const heap = wasm.heap8u(); - let i = 0; - for(; i < nOut; ++i) heap[pOut + i] = (Math.random()*255000) & 0xFF; - return i; - }); - } - - //////////////////////////////////////////////////////////////////////// - // Set up OPFS sqlite3_io_methods... - inst = installMethod(oIom); - inst('xClose', async function(pFile){ - warn("xClose(",arguments,") uses await"); - const f = __opfsHandles[pFile]; - delete __opfsHandles[pFile]; - if(f.opfsHandle){ - await f.opfsHandle.close(); - if(f.deleteOnClose){ - // TODO - } - } - f.dispose(); - return 0; - }) - ('xRead', /*i(ppij)*/function(pFile,pDest,n,offset){ - /* int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) */ - try { - const f = __opfsHandles[pFile]; - const heap = wasm.heap8u(); - const b = new Uint8Array(heap.buffer, pDest, n); - const nRead = f.opfsHandle.read(b, {at: offset}); - if(nRead SQLITE_DEFAULT_SECTOR_SIZE */; - //}) - - const rc = capi.sqlite3_vfs_register(oVfs.pointer, 0); - if(rc){ - oVfs.dispose(); - toss("sqlite3_vfs_register(OPFS) failed with rc",rc); - } - capi.sqlite3_vfs_register.addReference(oVfs, oIom); - warn("End of (very incomplete) OPFS setup.", oVfs); - //oVfs.dispose()/*only because we can't yet do anything with it*/; - -}/*initOpfsBits()*/; - -(async function(){ - importScripts('sqlite3.js'); - - const test1 = function(db){ - db.exec("create table if not exists t(a);") - .transaction(function(db){ - db.prepare("insert into t(a) values(?)") - .bind(new Date().getTime()) - .stepFinalize(); - stdout("Number of values in table t:", - db.selectValue("select count(*) from t")); - }); - }; - - const runTests = async function(Module){ - //stdout("Module",Module); - self._MODULE = Module /* this is only to facilitate testing from the console */; - const sqlite3 = Module.sqlite3, - capi = sqlite3.capi, - oo = sqlite3.oo1, - wasm = capi.wasm; - stdout("Loaded sqlite3:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); - - if(1){ - let errCount = 0; - [ - 'FileSystemHandle', 'FileSystemFileHandle', 'FileSystemDirectoryHandle', - 'FileSystemSyncAccessHandle' - ].forEach(function(n){ - const f = self[n]; - if(f){ - warn(n,f); - warn(n+'.prototype',f.prototype); - }else{ - stderr("MISSING",n); - ++errCount; - } - }); - if(errCount) return; - } - warn('self',self); - await initOpfsBits(sqlite3); - - if(1) return; - - let persistentDir; - if(1){ - persistentDir = ''; - }else{ - persistentDir = capi.sqlite3_web_persistent_dir(); - if(persistentDir){ - stderr("Persistent storage dir:",persistentDir); - }else{ - stderr("No persistent storage available."); - return; - } - } - const startTime = performance.now(); - let db; - try { - db = new oo.DB(persistentDir+'/foo.db'); - stdout("DB filename:",db.filename,db.fileName()); - const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', - banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'; - [ - test1 - ].forEach((f)=>{ - const n = performance.now(); - stdout(banner1,"Running",f.name+"()..."); - f(db, sqlite3, Module); - stdout(banner2,f.name+"() took ",(performance.now() - n),"ms"); - }); - }finally{ - if(db) db.close(); - } - stdout("Total test time:",(performance.now() - startTime),"ms"); - }; - - sqlite3InitModule(self.sqlite3TestModule).then(runTests); -})(); diff --git a/ext/wasm/scratchpad-wasmfs-main.js b/ext/wasm/scratchpad-wasmfs-main.js index 9f7d3fb1d8..bb0edbcfbc 100644 --- a/ext/wasm/scratchpad-wasmfs-main.js +++ b/ext/wasm/scratchpad-wasmfs-main.js @@ -52,7 +52,7 @@ let db; try { db = new oo.DB(persistentDir+'/foo.db'); - stdout("DB filename:",db.filename,db.fileName()); + stdout("DB filename:",db.filename,db.getFilename()); const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'; [ diff --git a/ext/wasm/sqlite3-opfs-async-proxy.js b/ext/wasm/sqlite3-opfs-async-proxy.js index 00b4025569..5969962d7a 100644 --- a/ext/wasm/sqlite3-opfs-async-proxy.js +++ b/ext/wasm/sqlite3-opfs-async-proxy.js @@ -164,7 +164,7 @@ const vfsAsyncImpls = { storeAndNotify(opName, state.sq3Codes.SQLITE_NOFOUND); } }, - xDelete: async function({filename, syncDir/*ignored*/}){ + xDeleteNoWait: async function({filename, syncDir, recursive = false}){ /* The syncDir flag is, for purposes of the VFS API's semantics, ignored here. However, if it has the value 0x1234 then: after deleting the given file, recursively try to delete any empty @@ -178,12 +178,13 @@ const vfsAsyncImpls = { is false. */ log("xDelete(",arguments[0],")"); + let rc = 0; try { while(filename){ const [hDir, filenamePart] = await getDirForPath(filename, false); //log("Removing:",hDir, filenamePart); if(!filenamePart) break; - await hDir.removeEntry(filenamePart); + await hDir.removeEntry(filenamePart, {recursive}); if(0x1234 !== syncDir) break; filename = getResolvedPath(filename, true); filename.pop(); @@ -193,8 +194,23 @@ const vfsAsyncImpls = { /* Ignoring: _presumably_ the file can't be found or a dir is not empty. */ //error("Delete failed",filename, e.message); + rc = state.sq3Codes.SQLITE_IOERR_DELETE; } - storeAndNotify('xDelete', 0); + return rc; + }, + xDelete: async function(...args){ + const rc = await vfsAsyncImpls.xDeleteNoWait(...args); + storeAndNotify('xDelete', rc); + }, + mkdir: async function(dirname){ + let rc = 0; + try { + await getDirForPath(dirname+"/filepart", true); + }catch(e){ + //error("mkdir failed",filename, e.message); + rc = state.sq3Codes.SQLITE_IOERR; + } + storeAndNotify('mkdir', rc); }, xFileSize: async function(fid){ log("xFileSize(",arguments,")"); diff --git a/ext/wasm/test-opfs-vfs.html b/ext/wasm/test-opfs-vfs.html index 6e470ecc88..41b278c289 100644 --- a/ext/wasm/test-opfs-vfs.html +++ b/ext/wasm/test-opfs-vfs.html @@ -15,8 +15,10 @@ very much incomplete, under construction, and experimental. See the dev console for all output. -
    +
    + Use this link to delete the persistent OPFS-side db (if any).
    +
    diff --git a/ext/wasm/test-opfs-vfs.js b/ext/wasm/test-opfs-vfs.js index fd71c9cdcf..04497c1c14 100644 --- a/ext/wasm/test-opfs-vfs.js +++ b/ext/wasm/test-opfs-vfs.js @@ -34,12 +34,16 @@ const tryOpfsVfs = function(sqlite3){ const oVfs = capi.sqlite3_vfs.instanceForPointer(pVfs) || toss("Unexpected instanceForPointer() result.");; log("OPFS VFS:",pVfs, oVfs); + const urlArgs = new URL(self.location.href).searchParams; const dbFile = "my-persistent.db"; - const db = new sqlite3.oo1.DB(dbFile, "c", "opfs"); + if(urlArgs.has('delete')) sqlite3.opfs.deleteEntry(dbFile); + + const opfs = sqlite3.opfs; + const db = new opfs.OpfsDb(dbFile); log("db file:",db.filename); try{ - let n = db.selectValue("select count(*) from sqlite_schema"); - if(n){ + if(opfs.entryExists(dbFile)){ + let n = db.selectValue("select count(*) from sqlite_schema"); log("Persistent data found. sqlite_schema entry count =",n); } db.transaction((db)=>{ @@ -54,6 +58,17 @@ const tryOpfsVfs = function(sqlite3){ }); }); log("count(*) from t =",db.selectValue("select count(*) from t")); + + // Some sanity checks of the opfs utility functions... + const testDir = '/sqlite3-opfs-'+opfs.randomFilename(12); + const aDir = testDir+'/test/dir'; + opfs.mkdir(aDir) || toss("mkdir failed"); + opfs.mkdir(aDir) || toss("mkdir must pass if the dir exists"); + opfs.deleteEntry(testDir+'/test') && toss("delete 1 should have failed (dir not empty)"); + opfs.deleteEntry(testDir+'/test/dir') || toss("delete 2 failed"); + opfs.deleteEntry(testDir+'/test/dir') && toss("delete 2b should have failed (dir already deleted)"); + opfs.deleteEntry(testDir,true) || toss("delete 3 failed"); + opfs.entryExists(testDir) && toss("entryExists(",testDir,") should have failed"); }finally{ db.close(); } @@ -62,10 +77,9 @@ const tryOpfsVfs = function(sqlite3){ }/*tryOpfsVfs()*/; importScripts('sqlite3.js'); -self.sqlite3InitModule().then((EmscriptenModule)=>{ - EmscriptenModule.sqlite3.installOpfsVfs() - .then((sqlite3)=>tryOpfsVfs(sqlite3)) - .catch((e)=>{ - console.error("Error initializing OPFS VFS:",e); - }); -}); +self.sqlite3InitModule() + .then((EmscriptenModule)=>EmscriptenModule.sqlite3.installOpfsVfs()) + .then((sqlite3)=>tryOpfsVfs(sqlite3)) + .catch((e)=>{ + console.error("Error initializing module:",e); + }); diff --git a/ext/wasm/testing1.js b/ext/wasm/testing1.js index e45b52bfa0..5743719096 100644 --- a/ext/wasm/testing1.js +++ b/ext/wasm/testing1.js @@ -1057,7 +1057,7 @@ let dbName = "/testing1.sqlite3"; let vfsName = undefined; - if(capi.sqlite3_web_db_is_kvvfs()){ + if(capi.sqlite3_web_db_uses_vfs(0,"kvvfs")){ dbName = "local"; vfsName = 'kvvfs'; logHtml("Found kvvfs. Clearing db(s) from sessionStorage and localStorage", @@ -1065,9 +1065,9 @@ clearKvvfs(); } const db = new oo.DB(dbName,'c',vfsName), startTime = performance.now(); - log("capi.sqlite3_web_db_is_kvvfs() ==",capi.sqlite3_web_db_is_kvvfs(db.pointer)); + log("db is kvvfs?",capi.sqlite3_web_db_uses_vfs(db.pointer,"kvvfs")); try { - log("db.filename =",db.filename,"db.fileName() =",db.fileName()); + log("db.filename =",db.filename,"db.fileName() =",db.getFilename()); const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'; [ diff --git a/manifest b/manifest index ab3e8572f1..adc88dd13b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Move\sthe\sOPFS\sVFS\sbits\sback\sinto\sapi/sqlite3-api-opfs.js.\sRefactor\sthe\sOPFS\sVFS\sinit\sprocess\sto\suse\sa\sPromise-returning\sfunction\swhich\sthe\sclient\smust\scall,\sas\sthat\seliminates\sany\suncertainty\sabout\swhen\sthe\sVFS\s(necessarily\sactivated\sasynchronously)\sactually\sbecomes\savailable\sto\sthe\sclient.\sRename\sx-sync-async.*\sto\stest-opfs-vfs.*\sMilestone:\sfirst\ssuccessful\stest\sof\sOPFS\swithout\sWASMFS. -D 2022-09-18T03:05:55.278 +C Numerous\scleanups\sin\sthe\sJS\sbits.\sRemoved\ssome\snow-defunct\swasm\stest\sfiles.\sExpose\ssqlite3.opfs\sobject\scontaining\svarious\sOPFS-specific\sutilities. +D 2022-09-18T17:32:35.336 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -483,10 +483,10 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 -F ext/wasm/api/sqlite3-api-oo1.js d7526517f7ad3f6bda16ad66d373bbb71b43168deef7af60eda5c9fe873d1387 -F ext/wasm/api/sqlite3-api-opfs.js 87d98f2449d5790efd7044e492166e4ed767e3320a03ed5a173b2b9364fc4896 -F ext/wasm/api/sqlite3-api-prologue.js 48ebca4ae340b0242d4f39bbded01bd0588393c8023628be1c454b4db6f7bd6e -F ext/wasm/api/sqlite3-api-worker1.js d33062afa045fd4be01ba4abc266801807472558b862b30056211b00c9c347b4 +F ext/wasm/api/sqlite3-api-oo1.js 2d13dddf0d2b4168a9249f124134d37924331e5b55e05dba18b6d661fbeefe48 +F ext/wasm/api/sqlite3-api-opfs.js 4090abf4e16b460543ff665e96822048e37a2703e0ba46a01fed3a15c024c034 +F ext/wasm/api/sqlite3-api-prologue.js 4e3e26880d444000cca1b4f3ddfa9d49581dfecd1de9426080239ecc208c447d +F ext/wasm/api/sqlite3-api-worker1.js e8456bd9b93eab297d065b25cb7a253835f606f9349383f2aa5c585e8d3b3aef F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 4130e2df9587f4e4c3afc04c3549d682c8a5c0cfe5b22819a0a86edb7f01b9bd F ext/wasm/batch-runner-kvvfs.html ef3b2f553abad4f17a2a29ce6526023793a88e8597b7a24b8c7855a030b90a16 @@ -502,7 +502,7 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 -F ext/wasm/index.html 492eb6c9023c9cda391c61702a28bd18e6c986ca2588ec3f384275bb77275285 +F ext/wasm/index.html d698cc021c25ca940f67805c2cc2848c303705d98b4c4f9f55565b9a9c37d2bb F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f @@ -510,11 +510,8 @@ F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e5 F ext/wasm/kvvfs.make 4b2ba6d061f3a52da9f5812f86f4faa80fb4d9456a152f6b0585dccd667a4e22 F ext/wasm/kvvfs1.html 13bb24190bfb276a57b228499519badcc1bf39ed07e4b37bc2a425ce6418fed1 F ext/wasm/kvvfs1.js ec1c1d071bb055711f9151df05616111432cf3e6bf7ac7f8dcbcfb56c9d9ed48 -F ext/wasm/scratchpad-opfs-worker.html 5fdda167571264300f388847d34f00b77dd48984a8dba2ee9c099c3ffa05db66 -F ext/wasm/scratchpad-opfs-worker.js cf6c4554d3b099c1a50013e50d19b3dc60e183511b4b4dbe7fabc2b9d3360567 -F ext/wasm/scratchpad-opfs-worker2.js 8c980370bbd5a262d96af8627c443936e11b87d0263a02123769d5953fc146da F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06 -F ext/wasm/scratchpad-wasmfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e +F ext/wasm/scratchpad-wasmfs-main.js f0836e3576df7a89390d777bb53e142e559e8a79becfb2a5a976490b05a1c4fa F ext/wasm/speedtest1-kvvfs.html c8b65c20e2b35298dc02d8e0a394d5e1eb857fd22e504468388234aee13aef08 F ext/wasm/speedtest1-wasmfs.html 6a67a6812f03a2058eb5c6ad0c8dea4bf749d0160ed9d6b826dabe7b766c3cf7 F ext/wasm/speedtest1-worker.html d8881ae802d15fb8adb94049265173e99f350e07e1d4e6f9e1cbd8969fe63a04 @@ -523,15 +520,15 @@ F ext/wasm/speedtest1.html fbb8e4d1639028443f3687a683be660beca6927920545cf6b1fdf F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 -F ext/wasm/sqlite3-opfs-async-proxy.js 456bef1253fd4732f133b601a4450b7f8461e67af6e8d30bf8a239ad775c77a2 +F ext/wasm/sqlite3-opfs-async-proxy.js 6e89e1af7c616afdd877cbcf5d0ec3d5f47ba252b9a19e696140b9495dc1e653 F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e -F ext/wasm/test-opfs-vfs.html 3e11c875c28f041891deeea6b2375121845ee1269cac6747df957ec0c7d4d37a w ext/wasm/x-sync-async.html -F ext/wasm/test-opfs-vfs.js bf70cd553a443b4eda63b577787ef73144f879fa062a20a73bb44e3242c81a15 w ext/wasm/x-sync-async.js +F ext/wasm/test-opfs-vfs.html eb69dda21eb414b8f5e3f7c1cc0f774103cc9c0f87b2d28a33419e778abfbab5 +F ext/wasm/test-opfs-vfs.js 753c6b86dd8ce0813121add44726a038ba1b83acebdc8414189cb163faf23e6d F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 F ext/wasm/testing-worker1-promiser.js 63448fddfd3b8c89ff667d17c8b31c6c2259dd4647ebbbd28f3a921c48e924da F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae -F ext/wasm/testing1.js 7cd8ab255c238b030d928755ae8e91e7d90a12f2ae601b1b8f7827aaa4fb258e +F ext/wasm/testing1.js 507001a970fe8a8eb67b6c8d783e1c1daa3db2719f727c4551af29349410e538 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c F ext/wasm/wasmfs.make 21a5cf297954a689e0dc2a95299ae158f681cae5e90c10b99d986097815fd42d @@ -2030,8 +2027,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 1c660970d0f62bcfd6e698a72b050d99972a1e39f45a5ac24194a190f8f78ab3 -R 039a2cd7ec4da87358ad6184da91bc68 +P b2abf60dbfa6648f671a3932cb65feb28d05a0d5b7f792351d14f9c13d9798c5 +R 79bf69f52699f5d039971f9fe79e1ec3 U stephan -Z 58e9ce410c47c756bc153984b857c4cf +Z 77273ebd9a43ebba5449759a950a89f3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d80c68d21c..6e29cdca81 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b2abf60dbfa6648f671a3932cb65feb28d05a0d5b7f792351d14f9c13d9798c5 \ No newline at end of file +26e625d05d9820033b23536f18ad3ddc59ed712ad507d4b0c7fe88abd15d2be8 \ No newline at end of file From ed85333977d5d2f5ce36ba2b04a9729fabad73bb Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 19 Sep 2022 00:40:53 +0000 Subject: [PATCH 129/428] Disable build of wasmfs-using components on aarch64 (ARM), as wasmfs won't build there. FossilOrigin-Name: 9a9eeebc2c27b734041089131b4952d7c0440df48ef32f355641aca61d4b30a0 --- ext/wasm/GNUmakefile | 11 +++++++++++ manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 20354a6b5f..730c31642b 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -435,4 +435,15 @@ oz: ######################################################################## # Sub-makes... include kvvfs.make + +######################################################################## +# Some platforms do not support the WASMFS build. Raspberry Pi OS is one +# of them. As such platforms are discovered, add their (uname -m) name +# to PLATFORMS_WITH_NO_WASMFS to exclude the wasmfs build parts. +PLATFORMS_WITH_NO_WASMFS := aarch64 # add any others here +THIS_ARCH := $(shell /usr/bin/uname -m) +ifneq (,$(filter $(THIS_ARCH),$(PLATFORMS_WITH_NO_WASMFS))) +$(info This platform does not support the WASMFS build.) +else include wasmfs.make +endif diff --git a/manifest b/manifest index adc88dd13b..59f75f956d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Numerous\scleanups\sin\sthe\sJS\sbits.\sRemoved\ssome\snow-defunct\swasm\stest\sfiles.\sExpose\ssqlite3.opfs\sobject\scontaining\svarious\sOPFS-specific\sutilities. -D 2022-09-18T17:32:35.336 +C Disable\sbuild\sof\swasmfs-using\scomponents\son\saarch64\s(ARM),\sas\swasmfs\swon't\sbuild\sthere. +D 2022-09-19T00:40:53.768 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 24e5802cc186b492b3ef6290b64b857e51aa0afaa929b74a68f9fb457c4e8814 +F ext/wasm/GNUmakefile 19ba3304cf4e5e5f480a5c3701b2c7d7b7383f70416359fc4a1d304b4ffdd895 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 150a793a47205b8009ac934f3b6d6ebf67b965c072339aaa25ce808a19e116cc F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -2027,8 +2027,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P b2abf60dbfa6648f671a3932cb65feb28d05a0d5b7f792351d14f9c13d9798c5 -R 79bf69f52699f5d039971f9fe79e1ec3 +P 26e625d05d9820033b23536f18ad3ddc59ed712ad507d4b0c7fe88abd15d2be8 +R 21836a2cce93d296ed51495c106454e4 U stephan -Z 77273ebd9a43ebba5449759a950a89f3 +Z 476194268503f5b6d3efae07b951acc1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6e29cdca81..2d549f8541 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -26e625d05d9820033b23536f18ad3ddc59ed712ad507d4b0c7fe88abd15d2be8 \ No newline at end of file +9a9eeebc2c27b734041089131b4952d7c0440df48ef32f355641aca61d4b30a0 \ No newline at end of file From ac51eb77546653a6e0563adc0f3460ae69744485 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 19 Sep 2022 03:57:31 +0000 Subject: [PATCH 130/428] Rename demo-oo1.* to demo-123.* and add demo-123-worker.html, which runs the same demo via a Worker. Doc typo fixes. FossilOrigin-Name: 2e4a005bd35424caeaa99ace23162cf79e2ebdb159475ffad92b85dc864ad764 --- ext/wasm/api/sqlite3-api-worker1.js | 7 +- ext/wasm/demo-123-worker.html | 40 +++++++++ ext/wasm/demo-123.html | 24 ++++++ ext/wasm/{demo-oo1.js => demo-123.js} | 118 ++++++++++++++++---------- ext/wasm/demo-oo1.html | 34 -------- ext/wasm/index.html | 14 ++- ext/wasm/sqlite3-worker1-promiser.js | 33 ++++--- manifest | 21 ++--- manifest.uuid | 2 +- 9 files changed, 181 insertions(+), 112 deletions(-) create mode 100644 ext/wasm/demo-123-worker.html create mode 100644 ext/wasm/demo-123.html rename ext/wasm/{demo-oo1.js => demo-123.js} (67%) delete mode 100644 ext/wasm/demo-oo1.html diff --git a/ext/wasm/api/sqlite3-api-worker1.js b/ext/wasm/api/sqlite3-api-worker1.js index 97f2677e6c..b41a837e9f 100644 --- a/ext/wasm/api/sqlite3-api-worker1.js +++ b/ext/wasm/api/sqlite3-api-worker1.js @@ -326,13 +326,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ sqlite3.initWorker1API = function(){ 'use strict'; const toss = (...args)=>{throw new Error(args.join(' '))}; - if('function' !== typeof importScripts){ - toss("Cannot initalize the sqlite3 worker API in the main thread."); + if(self.window === self || 'function' !== typeof importScripts){ + toss("initWorker1API() must be run from a Worker thread."); } const self = this.self; const sqlite3 = this.sqlite3 || toss("Missing this.sqlite3 object."); - const SQLite3 = sqlite3.oo1 || toss("Missing this.sqlite3.oo1 OO API."); - const DB = SQLite3.DB; + const DB = sqlite3.oo1.DB; /** Returns the app-wide unique ID for the given db, creating one if diff --git a/ext/wasm/demo-123-worker.html b/ext/wasm/demo-123-worker.html new file mode 100644 index 0000000000..7480037a16 --- /dev/null +++ b/ext/wasm/demo-123-worker.html @@ -0,0 +1,40 @@ + + + + + + + Hello, sqlite3 + + + +

    1-2-sqlite3 worker demo

    + + + diff --git a/ext/wasm/demo-123.html b/ext/wasm/demo-123.html new file mode 100644 index 0000000000..25fa974910 --- /dev/null +++ b/ext/wasm/demo-123.html @@ -0,0 +1,24 @@ + + + + + + + Hello, sqlite3 + + + +

    1-2-sqlite3 demo

    + + + + diff --git a/ext/wasm/demo-oo1.js b/ext/wasm/demo-123.js similarity index 67% rename from ext/wasm/demo-oo1.js rename to ext/wasm/demo-123.js index 4564fe0ddd..7a4843a4d9 100644 --- a/ext/wasm/demo-oo1.js +++ b/ext/wasm/demo-123.js @@ -1,5 +1,5 @@ /* - 2022-08-16 + 2022-09-19 The author disclaims copyright to this source code. In place of a legal notice, here is a blessing: @@ -10,34 +10,48 @@ *********************************************************************** - A basic demonstration of the SQLite3 OO API #1, shorn of assertions - and the like to improve readability. + A basic demonstration of the SQLite3 OO API. */ 'use strict'; (function(){ - const toss = function(...args){throw new Error(args.join(' '))}; - const debug = console.debug.bind(console), - log = console.log.bind(console), - warn = console.warn.bind(console), - error = console.error.bind(console); + /** + Set up our output channel differently depending + on whether we are running in a worker thread or + the main (UI) thread. + */ + let logHtml; + if(self.window === self /* UI thread */){ + logHtml = function(cssClass,...args){ + const ln = document.createElement('div'); + if(cssClass) ln.classList.add(cssClass); + ln.append(document.createTextNode(args.join(' '))); + document.body.append(ln); + }; + }else{ /* Worker thread */ + logHtml = function(cssClass,...args){ + postMessage({ + type:'log', + payload:{cssClass, args} + }); + }; + } + const log = (...args)=>logHtml('',...args); + const warn = (...args)=>logHtml('warning',...args); + const error = (...args)=>logHtml('error',...args); const demo1 = function(sqlite3){ - const capi = sqlite3.capi, - oo = sqlite3.oo1, - wasm = capi.wasm; - - const dbName = ( - 0 ? "" : capi.sqlite3_web_persistent_dir() - )+"/mydb.sqlite3" - if(0 && capi.sqlite3_web_persistent_dir()){ - capi.wasm.sqlite3_wasm_vfs_unlink(dbName); - } - const db = new oo.DB(dbName); - log("db =",db.filename); + const capi = sqlite3.capi/*C-style API*/, + oo = sqlite3.oo1/*high-level OO API*/; + log("sqlite3 version",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); + const db = new oo.DB("/mydb.sqlite3"); + log("transient b =",db.filename); /** Never(!) rely on garbage collection to clean up DBs and (especially) statements. Always wrap their lifetimes in - try/finally construct... + try/finally construct, as demonstrated below. By and large, + client code can avoid lifetime-related complications of + prepared statement objects by using the DB.exec() method for + SQL execution. */ try { log("Create a table..."); @@ -48,10 +62,13 @@ // ... numerous other options ... }); // SQL can be either a string or a byte array + // or an array of strings which get concatenated + // together as-is (so be sure to end each statement + // with a semicolon). log("Insert some data using exec()..."); let i; - for( i = 1; i <= 5; ++i ){ + for( i = 20; i <= 25; ++i ){ db.exec({ sql: "insert into t(a,b) values (?,?)", // bind by parameter index... @@ -60,7 +77,7 @@ db.exec({ sql: "insert into t(a,b) values ($a,$b)", // bind by parameter name... - bind: {$a: i * 3, $b: i * 4} + bind: {$a: i * 10, $b: i * 20} }); } @@ -93,7 +110,7 @@ sql: "select a as aa, b as bb from t order by aa limit 3", rowMode: 'object', callback: function(row){ - log("row ",++this.counter,"=",row); + log("row ",++this.counter,"=",JSON.stringify(row)); }.bind({counter: 0}) }); @@ -115,6 +132,15 @@ }.bind({counter: 0}) }); + log("Query data with exec() using rowMode $COLNAME (result column name)..."); + db.exec({ + sql: "select a a, b from t order by a limit 3", + rowMode: '$a', + callback: function(value){ + log("row ",++this.counter,"a =",value); + }.bind({counter: 0}) + }); + log("Query data with exec() without a callback..."); let resultRows = []; db.exec({ @@ -122,7 +148,7 @@ rowMode: 'object', resultRows: resultRows }); - log("Result rows:",resultRows); + log("Result rows:",JSON.stringify(resultRows,undefined,2)); log("Create a scalar UDF..."); db.createFunction({ @@ -144,9 +170,12 @@ }); log("Result column names:",columnNames); - if(0){ - warn("UDF will throw because of incorrect arg count..."); + try{ + log("The following use of the twice() UDF will", + "fail because of incorrect arg count..."); db.exec("select twice(1,2,3)"); + }catch(e){ + warn("Got expected exception:",e.message); } try { @@ -170,7 +199,7 @@ log("In savepoint: count(*) from t =",db.selectValue("select count(*) from t")); D.savepoint(function(DD){ const rows = []; - D.exec({ + DD.exec({ sql: ["insert into t(a,b) values(99,100);", "select count(*) from t"], rowMode: 0, @@ -188,17 +217,17 @@ throw e; } } - }finally{ db.close(); } + log("That's all, folks!"); + /** - Misc. DB features: + Some of the features of the OO API not demonstrated above... - get change count (total or statement-local, 32- or 64-bit) - - get its file name - - selectValue() takes SQL and returns first column of first row. + - get a DB's file name Misc. Stmt features: @@ -213,20 +242,21 @@ */ }/*demo1()*/; - const runDemos = function(Module){ - //log("Module.sqlite3",Module); - const sqlite3 = Module.sqlite3, - capi = sqlite3.capi; - log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); - log("sqlite3 namespace:",sqlite3); + log("Loading and initializing sqlite3 module..."); + if(self.window!==self) /*worker thread*/{ + importScripts("sqlite3.js"); + } + self.sqlite3InitModule({ + // We can redirect any stdout/stderr from the module + // like so... + print: log, + printErr: error + }).then(function(EmscriptenModule){ + log("Done initializing. Running demo..."); try { - demo1(sqlite3); + demo1(EmscriptenModule.sqlite3); }catch(e){ error("Exception:",e.message); - throw e; } - }; - - //self.sqlite3TestModule.sqlite3ApiConfig.persistentDirName = "/hi"; - self.sqlite3TestModule.initSqlite3().then(runDemos); + }); })(); diff --git a/ext/wasm/demo-oo1.html b/ext/wasm/demo-oo1.html deleted file mode 100644 index 9b6e8cbfa3..0000000000 --- a/ext/wasm/demo-oo1.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - sqlite3-api OO #1 Demo - - -
    sqlite3-api OO #1 Demo
    - -
    -
    -
    Initializing app...
    -
    - 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. -
    -
    -
    Downloading...
    -
    - -
    -
    Most stuff on this page happens in the dev console.
    -
    -
    - - - - - diff --git a/ext/wasm/index.html b/ext/wasm/index.html index d13f9bb7f3..68b43d7652 100644 --- a/ext/wasm/index.html +++ b/ext/wasm/index.html @@ -22,20 +22,30 @@ the web server emit the so-called COOP and COEP headers. The default build of althttpd does not.
  • +
  • Any OPFS-related pages require very recent + version of Chrome or Chromium (v102 at least, possibly + newer). OPFS support in the other major browsers is + pending.
  • Whether or not WASMFS/OPFS support is enabled on any given page may depend on build-time options which are off by default because they currently (as of 2022-09-08) break - with Worker-based pages. + with Worker-based pages. Similarly, WASMFS does not work on + some platforms, e.g. Raspberry Pi 4.
  • The tests...
      +
    • demo-123 provides a + no-nonsense example of adding sqlite3 support to a + web page.
    • +
    • demo-123-worker is the + same as demo-123 but loads and run sqlite3 from + a Worker thread.
    • testing1: sanity tests of the core APIs and surrounding utility code.
    • testing2: Worker-based test of OO API #1.
    • testing-worker1-promiser: tests for the Promise-based wrapper of the Worker-based API.
    • -
    • demo-oo1: demonstration of the OO API #1.
    • batch-runner: runs batches of SQL exported from speedtest1.
    • batch-runner-kvvfs: KVVFS-specific variant of batch-runner.html.
    • speedtest1: a main-thread WASM build of speedtest1.
    • diff --git a/ext/wasm/sqlite3-worker1-promiser.js b/ext/wasm/sqlite3-worker1-promiser.js index 7327e14c70..37a4b5962f 100644 --- a/ext/wasm/sqlite3-worker1-promiser.js +++ b/ext/wasm/sqlite3-worker1-promiser.js @@ -42,7 +42,7 @@ arguments when the worker fires its initial 'sqlite3-api'/'worker1-ready' message, which it does when sqlite3.initWorker1API() completes its initialization. This is - the simplest way to tell the worker to kick of work at the + the simplest way to tell the worker to kick off work at the earliest opportunity. - `onerror` (optional): a callback to pass error-type events from @@ -55,18 +55,18 @@ are not handled by this proxy. Ideally that "should" never happen, as this proxy aims to handle all known message types. - - `generateMessageId` (optional): a function which, when passed - an about-to-be-posted message object, generates a _unique_ - message ID for the message, which this API then assigns as the - messageId property of the message. It _must_ generate unique - IDs so that dispatching can work. If not defined, a default - generator is used. + - `generateMessageId` (optional): a function which, when passed an + about-to-be-posted message object, generates a _unique_ message ID + for the message, which this API then assigns as the messageId + property of the message. It _must_ generate unique IDs on each call + so that dispatching can work. If not defined, a default generator + is used (which should be sufficient for most or all cases). - `dbId` (optional): is the database ID to be used by the worker. This must initially be unset or a falsy value. The first `open` message sent to the worker will cause this config entry to be assigned to the ID of the opened database. That ID - "should" be set as the `dbId` property of the message sent in + "should" be set as the `dbId` property of the messages sent in future requests, so that the worker uses that database. However, if the worker is not given an explicit dbId, it will use the first-opened database by default. If client code needs @@ -80,7 +80,6 @@ - `debug` (optional): a console.debug()-style function for logging information about messages. - This function returns a stateful factory function with the following interfaces: @@ -90,13 +89,13 @@ The first form expects the "type" and "args" values for a Worker message. The second expects an object in the form {type:..., args:...} plus any other properties the client cares to set. This - function will always set the messageId property on the object, - even if it's already set, and will set the dbId property to - config.dbId if it is _not_ set in the message object. + function will always set the `messageId` property on the object, + even if it's already set, and will set the `dbId` property to + `config.dbId` if it is _not_ set in the message object. The function throws on error. - The function installs a temporarily message listener, posts a + The function installs a temporary message listener, posts a message to the configured Worker, and handles the message's response via the temporary message listener. The then() callback of the returned Promise is passed the `message.data` property from @@ -108,14 +107,14 @@ ``` const config = {...}; - const eventPromiser = sqlite3Worker1Promiser(config); - eventPromiser('open', {filename:"/foo.db"}).then(function(msg){ + const sq3Promiser = sqlite3Worker1Promiser(config); + sq3Promiser('open', {filename:"/foo.db"}).then(function(msg){ console.log("open response",msg); // => {type:'open', result: {filename:'/foo.db'}, ...} // Recall that config.dbId will be set for the first 'open' // call and cleared for a matching 'close' call. }); - eventPromiser({type:'close'}).then((msg)=>{ - console.log("open response",msg); // => {type:'open', result: {filename:'/foo.db'}, ...} + sq3Promiser({type:'close'}).then((msg)=>{ + console.log("close response",msg); // => {type:'close', result: {filename:'/foo.db'}, ...} // Recall that config.dbId will be used by default for the message's dbId if // none is explicitly provided, and a 'close' op will clear config.dbId if it // closes that exact db. diff --git a/manifest b/manifest index 59f75f956d..ce729f04c1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Disable\sbuild\sof\swasmfs-using\scomponents\son\saarch64\s(ARM),\sas\swasmfs\swon't\sbuild\sthere. -D 2022-09-19T00:40:53.768 +C Rename\sdemo-oo1.*\sto\sdemo-123.*\sand\sadd\sdemo-123-worker.html,\swhich\sruns\sthe\ssame\sdemo\svia\sa\sWorker.\sDoc\stypo\sfixes. +D 2022-09-19T03:57:31.704 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -486,7 +486,7 @@ F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03 F ext/wasm/api/sqlite3-api-oo1.js 2d13dddf0d2b4168a9249f124134d37924331e5b55e05dba18b6d661fbeefe48 F ext/wasm/api/sqlite3-api-opfs.js 4090abf4e16b460543ff665e96822048e37a2703e0ba46a01fed3a15c024c034 F ext/wasm/api/sqlite3-api-prologue.js 4e3e26880d444000cca1b4f3ddfa9d49581dfecd1de9426080239ecc208c447d -F ext/wasm/api/sqlite3-api-worker1.js e8456bd9b93eab297d065b25cb7a253835f606f9349383f2aa5c585e8d3b3aef +F ext/wasm/api/sqlite3-api-worker1.js ee4cf149cbacb63d06b536674f822aa5088b7e022cdffc69f1f36cebe2f9fea0 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 4130e2df9587f4e4c3afc04c3549d682c8a5c0cfe5b22819a0a86edb7f01b9bd F ext/wasm/batch-runner-kvvfs.html ef3b2f553abad4f17a2a29ce6526023793a88e8597b7a24b8c7855a030b90a16 @@ -496,13 +496,14 @@ F ext/wasm/common/SqliteTestUtil.js 529161a624265ba84271a52db58da022649832fa1c71 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 3a5143699c2b73a85b962271e1a9b3241b30d90e30d895e4f55665e648572962 F ext/wasm/common/whwasmutil.js f7282ef36c9625330d4e6e82d1beec6678cd101e95e7108cd85db587a788c145 -F ext/wasm/demo-oo1.html 75646855b38405d82781246fd08c852a2b3bee05dd9f0fe10ab655a8cffb79aa -F ext/wasm/demo-oo1.js 477f230cce3455e701431436d892d8c6bfea2bdf1ddcdd32a273e2f4bb339801 +F ext/wasm/demo-123-worker.html e419b66495d209b5211ec64903b4cfb3ca7df20d652b41fcd28bf018a773234f +F ext/wasm/demo-123.html aa281d33b7eefa755f3122b7b5a18f39a42dc5fb69c8879171bf14b4c37c4ec4 w ext/wasm/demo-oo1.html +F ext/wasm/demo-123.js 2c5e7775339624819067fd169d202004faeddc9d8ef452dd1f95aa4b3972a5b2 w ext/wasm/demo-oo1.js F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 -F ext/wasm/index.html d698cc021c25ca940f67805c2cc2848c303705d98b4c4f9f55565b9a9c37d2bb +F ext/wasm/index.html aed40adf52598a353e27e50480d53e59a5e75e9ba889f7d8bb2fda45a0a91c3b F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f @@ -521,7 +522,7 @@ F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f129 F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 F ext/wasm/sqlite3-opfs-async-proxy.js 6e89e1af7c616afdd877cbcf5d0ec3d5f47ba252b9a19e696140b9495dc1e653 -F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9 +F ext/wasm/sqlite3-worker1-promiser.js 4fd0465688a28a75f1d4ee4406540ba494f49844e3cad0670d0437a001943365 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/test-opfs-vfs.html eb69dda21eb414b8f5e3f7c1cc0f774103cc9c0f87b2d28a33419e778abfbab5 F ext/wasm/test-opfs-vfs.js 753c6b86dd8ce0813121add44726a038ba1b83acebdc8414189cb163faf23e6d @@ -2027,8 +2028,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 26e625d05d9820033b23536f18ad3ddc59ed712ad507d4b0c7fe88abd15d2be8 -R 21836a2cce93d296ed51495c106454e4 +P 9a9eeebc2c27b734041089131b4952d7c0440df48ef32f355641aca61d4b30a0 +R 8192577a9e0361d9ed4a520726bee437 U stephan -Z 476194268503f5b6d3efae07b951acc1 +Z 37e4874c91d49fe3eefc7a1b4b57c686 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2d549f8541..29452e9015 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9a9eeebc2c27b734041089131b4952d7c0440df48ef32f355641aca61d4b30a0 \ No newline at end of file +2e4a005bd35424caeaa99ace23162cf79e2ebdb159475ffad92b85dc864ad764 \ No newline at end of file From 8766fd20d147c224f4bb6029699bb412bf636182 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 19 Sep 2022 05:19:04 +0000 Subject: [PATCH 131/428] Replace OPFS VFS xSleep() impl with a more efficient one (no Worker round-trip needed). FossilOrigin-Name: b9773f164878b0a1b7c88cc7a6d1374ea95f64920065e8b2b178a1afffd82fe5 --- ext/wasm/api/sqlite3-api-opfs.js | 27 ++++++++++++++++----------- ext/wasm/sqlite3-opfs-async-proxy.js | 6 ------ manifest | 18 +++++++++--------- manifest.uuid | 2 +- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-opfs.js b/ext/wasm/api/sqlite3-api-opfs.js index ede4fb32b1..d6a9dbd22f 100644 --- a/ext/wasm/api/sqlite3-api-opfs.js +++ b/ext/wasm/api/sqlite3-api-opfs.js @@ -255,10 +255,6 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) 'cleanup default VFS wrapper', ()=>(dVfs ? dVfs.dispose() : null), 'cleanup opfsIoMethods', ()=>opfsIoMethods.dispose() ]; - if(dVfs){ - opfsVfs.$xSleep = dVfs.$xSleep; - opfsVfs.$xRandomness = dVfs.$xRandomness; - } /** Pedantic sidebar about opfsVfs.ondispose: the entries in that array are items to clean up when opfsVfs.dispose() is called, but in this @@ -468,6 +464,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) warn("OPFS xGetLastError() has nothing sensible to return."); return 0; }, + //xSleep is optionally defined below xOpen: function f(pVfs, zName, pFile, flags, pOutFlags){ if(!f._){ f._ = { @@ -522,6 +519,10 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) }/*xOpen()*/ }/*vfsSyncWrappers*/; + if(dVfs){ + opfsVfs.$xRandomness = dVfs.$xRandomness; + opfsVfs.$xSleep = dVfs.$xSleep; + } if(!opfsVfs.$xRandomness){ /* If the default VFS has no xRandomness(), add a basic JS impl... */ vfsSyncWrappers.xRandomness = function(pVfs, nOut, pOut){ @@ -533,10 +534,12 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) } if(!opfsVfs.$xSleep){ /* If we can inherit an xSleep() impl from the default VFS then - use it, otherwise install one which is certainly less accurate - because it has to go round-trip through the async worker, but - provides the only option for a synchronous sleep() in JS. */ - vfsSyncWrappers.xSleep = (pVfs,ms)=>opRun('xSleep',ms); + assume it's sane and use it, otherwise install a JS-based + one. */ + vfsSyncWrappers.xSleep = function(pVfs,ms){ + Atomics.wait(state.opSABView, state.opIds.xSleep, 0, ms); + return 0; + }; } /* Install the vfs/io_methods into their C-level shared instances... */ @@ -545,7 +548,6 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) inst = installMethod(opfsVfs); for(let k of Object.keys(vfsSyncWrappers)) inst(k, vfsSyncWrappers[k]); - /** Syncronously deletes the given OPFS filesystem entry, ignoring any errors. As this environment has no notion of "current @@ -646,8 +648,11 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) let jRead = wasm.cstringToJs(readBuf); log("xRead() got:",jRead); if("sanity"!==jRead) toss("Unexpected xRead() value."); - log("xSleep()ing before close()ing..."); - opRun('xSleep',1000); + if(vfsSyncWrappers.xSleep){ + log("xSleep()ing before close()ing..."); + vfsSyncWrappers.xSleep(opfsVfs.pointer,2000); + log("waking up from xSleep()"); + } rc = ioSyncWrappers.xClose(fid); log("xClose rc =",rc,"opSABView =",state.opSABView); log("Deleting file:",dbFile); diff --git a/ext/wasm/sqlite3-opfs-async-proxy.js b/ext/wasm/sqlite3-opfs-async-proxy.js index 5969962d7a..3ae6af043a 100644 --- a/ext/wasm/sqlite3-opfs-async-proxy.js +++ b/ext/wasm/sqlite3-opfs-async-proxy.js @@ -287,12 +287,6 @@ const vfsAsyncImpls = { } storeAndNotify('xRead',rc); }, - xSleep: async function f(ms){ - log("xSleep(",ms,")"); - await new Promise((resolve)=>{ - setTimeout(()=>resolve(), ms); - }).finally(()=>storeAndNotify('xSleep',0)); - }, xSync: async function({fid,flags/*ignored*/}){ log("xSync(",arguments[0],")"); const fh = __openFiles[fid]; diff --git a/manifest b/manifest index ce729f04c1..5116b71772 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Rename\sdemo-oo1.*\sto\sdemo-123.*\sand\sadd\sdemo-123-worker.html,\swhich\sruns\sthe\ssame\sdemo\svia\sa\sWorker.\sDoc\stypo\sfixes. -D 2022-09-19T03:57:31.704 +C Replace\sOPFS\sVFS\sxSleep()\simpl\swith\sa\smore\sefficient\sone\s(no\sWorker\sround-trip\sneeded). +D 2022-09-19T05:19:04.566 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -484,7 +484,7 @@ F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 F ext/wasm/api/sqlite3-api-oo1.js 2d13dddf0d2b4168a9249f124134d37924331e5b55e05dba18b6d661fbeefe48 -F ext/wasm/api/sqlite3-api-opfs.js 4090abf4e16b460543ff665e96822048e37a2703e0ba46a01fed3a15c024c034 +F ext/wasm/api/sqlite3-api-opfs.js d44f724990e99e242725c6623d6e1fbf843addabac13aabcd8adc8b7a02cb8b6 F ext/wasm/api/sqlite3-api-prologue.js 4e3e26880d444000cca1b4f3ddfa9d49581dfecd1de9426080239ecc208c447d F ext/wasm/api/sqlite3-api-worker1.js ee4cf149cbacb63d06b536674f822aa5088b7e022cdffc69f1f36cebe2f9fea0 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 @@ -497,8 +497,8 @@ F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/common/testing.css 3a5143699c2b73a85b962271e1a9b3241b30d90e30d895e4f55665e648572962 F ext/wasm/common/whwasmutil.js f7282ef36c9625330d4e6e82d1beec6678cd101e95e7108cd85db587a788c145 F ext/wasm/demo-123-worker.html e419b66495d209b5211ec64903b4cfb3ca7df20d652b41fcd28bf018a773234f -F ext/wasm/demo-123.html aa281d33b7eefa755f3122b7b5a18f39a42dc5fb69c8879171bf14b4c37c4ec4 w ext/wasm/demo-oo1.html -F ext/wasm/demo-123.js 2c5e7775339624819067fd169d202004faeddc9d8ef452dd1f95aa4b3972a5b2 w ext/wasm/demo-oo1.js +F ext/wasm/demo-123.html aa281d33b7eefa755f3122b7b5a18f39a42dc5fb69c8879171bf14b4c37c4ec4 +F ext/wasm/demo-123.js 2c5e7775339624819067fd169d202004faeddc9d8ef452dd1f95aa4b3972a5b2 F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 @@ -521,7 +521,7 @@ F ext/wasm/speedtest1.html fbb8e4d1639028443f3687a683be660beca6927920545cf6b1fdf F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 -F ext/wasm/sqlite3-opfs-async-proxy.js 6e89e1af7c616afdd877cbcf5d0ec3d5f47ba252b9a19e696140b9495dc1e653 +F ext/wasm/sqlite3-opfs-async-proxy.js 7806909f6957c6173843b53087b7f01fb41fb27f52bdad250fe2081d0fa44f9e F ext/wasm/sqlite3-worker1-promiser.js 4fd0465688a28a75f1d4ee4406540ba494f49844e3cad0670d0437a001943365 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/test-opfs-vfs.html eb69dda21eb414b8f5e3f7c1cc0f774103cc9c0f87b2d28a33419e778abfbab5 @@ -2028,8 +2028,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 9a9eeebc2c27b734041089131b4952d7c0440df48ef32f355641aca61d4b30a0 -R 8192577a9e0361d9ed4a520726bee437 +P 2e4a005bd35424caeaa99ace23162cf79e2ebdb159475ffad92b85dc864ad764 +R 7b4ce0e4c47ddeace77c92f8cdf4ccc9 U stephan -Z 37e4874c91d49fe3eefc7a1b4b57c686 +Z 2dc660cb87f9b9223b60fbf12ba2d753 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 29452e9015..1e0d5dc9cb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2e4a005bd35424caeaa99ace23162cf79e2ebdb159475ffad92b85dc864ad764 \ No newline at end of file +b9773f164878b0a1b7c88cc7a6d1374ea95f64920065e8b2b178a1afffd82fe5 \ No newline at end of file From 862281fc47187e37b10c1fbd46250de23ebef452 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 19 Sep 2022 09:25:25 +0000 Subject: [PATCH 132/428] Correct OPFS VFS xRead() to copy the result buffer if the result code is SQLITE_IOERR_SHORT_READ. FossilOrigin-Name: 56668f9902c6e896b6c63621a444444c6f58ee20f88a5feae97f1699be35892d --- ext/wasm/api/sqlite3-api-opfs.js | 7 ++++--- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-opfs.js b/ext/wasm/api/sqlite3-api-opfs.js index d6a9dbd22f..6446f2fb35 100644 --- a/ext/wasm/api/sqlite3-api-opfs.js +++ b/ext/wasm/api/sqlite3-api-opfs.js @@ -386,9 +386,10 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) try { // FIXME(?): block until we finish copying the xRead result buffer. How? rc = opRun('xRead',{fid:pFile, n, offset}); - if(0!==rc) return rc; - let i = 0; - for(; i < n; ++i) wasm.setMemValue(pDest + i, f.sabView[i]); + if(0===rc || capi.SQLITE_IOERR_SHORT_READ===rc){ + let i = 0; + for(; i < n; ++i) wasm.setMemValue(pDest + i, f.sabView[i]); + } }catch(e){ error("xRead(",arguments,") failed:",e,f); rc = capi.SQLITE_IOERR_READ; diff --git a/manifest b/manifest index 5116b71772..c678bc0b2d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Replace\sOPFS\sVFS\sxSleep()\simpl\swith\sa\smore\sefficient\sone\s(no\sWorker\sround-trip\sneeded). -D 2022-09-19T05:19:04.566 +C Correct\sOPFS\sVFS\sxRead()\sto\scopy\sthe\sresult\sbuffer\sif\sthe\sresult\scode\sis\sSQLITE_IOERR_SHORT_READ. +D 2022-09-19T09:25:25.676 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -484,7 +484,7 @@ F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 F ext/wasm/api/sqlite3-api-oo1.js 2d13dddf0d2b4168a9249f124134d37924331e5b55e05dba18b6d661fbeefe48 -F ext/wasm/api/sqlite3-api-opfs.js d44f724990e99e242725c6623d6e1fbf843addabac13aabcd8adc8b7a02cb8b6 +F ext/wasm/api/sqlite3-api-opfs.js 6a48568014dcadee22ff8eaa13deab573fbae362a71194efb6c1214d41d6b48d F ext/wasm/api/sqlite3-api-prologue.js 4e3e26880d444000cca1b4f3ddfa9d49581dfecd1de9426080239ecc208c447d F ext/wasm/api/sqlite3-api-worker1.js ee4cf149cbacb63d06b536674f822aa5088b7e022cdffc69f1f36cebe2f9fea0 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 @@ -2028,8 +2028,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 2e4a005bd35424caeaa99ace23162cf79e2ebdb159475ffad92b85dc864ad764 -R 7b4ce0e4c47ddeace77c92f8cdf4ccc9 +P b9773f164878b0a1b7c88cc7a6d1374ea95f64920065e8b2b178a1afffd82fe5 +R a6befd159a478cdd8d3c42193659027b U stephan -Z 2dc660cb87f9b9223b60fbf12ba2d753 +Z 2c8c2a7ce6b266c9bc7d1baf57207e7e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1e0d5dc9cb..e3f6dd88b5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b9773f164878b0a1b7c88cc7a6d1374ea95f64920065e8b2b178a1afffd82fe5 \ No newline at end of file +56668f9902c6e896b6c63621a444444c6f58ee20f88a5feae97f1699be35892d \ No newline at end of file From 509f40526e97c321720b3cf3430f8a4361e8e547 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 19 Sep 2022 09:58:01 +0000 Subject: [PATCH 133/428] Generic cleanups in the OPFS VFS proxies. FossilOrigin-Name: 7d5f6adc3b964413fc96ad8d2735312c3e58348024cabdd2099682cbf696eaf7 --- ext/wasm/api/sqlite3-api-opfs.js | 67 +++++++++++++++------------- ext/wasm/sqlite3-opfs-async-proxy.js | 22 ++++----- manifest | 14 +++--- manifest.uuid | 2 +- 4 files changed, 55 insertions(+), 50 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-opfs.js b/ext/wasm/api/sqlite3-api-opfs.js index 6446f2fb35..e0554e8c0c 100644 --- a/ext/wasm/api/sqlite3-api-opfs.js +++ b/ext/wasm/api/sqlite3-api-opfs.js @@ -76,51 +76,46 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ `opfs` property, containing several OPFS-specific utilities. */ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri){ + delete sqlite3.installOpfsVfs; + if(self.window===self || + !self.SharedArrayBuffer || + !self.FileSystemHandle || + !self.FileSystemDirectoryHandle || + !self.FileSystemFileHandle || + !self.FileSystemFileHandle.prototype.createSyncAccessHandle || + !navigator.storage.getDirectory){ + return Promise.reject( + new Error("This environment does not have OPFS support.") + ); + } const options = (asyncProxyUri && 'object'===asyncProxyUri) ? asyncProxyUri : { proxyUri: asyncProxyUri }; - const thisUrl = new URL(self.location.href); + const urlParams = new URL(self.location.href).searchParams; if(undefined===options.verbose){ - options.verbose = thisUrl.searchParams.has('opfs-verbose') ? 3 : 2; + options.verbose = urlParams.has('opfs-verbose') ? 3 : 2; } if(undefined===options.sanityChecks){ - options.sanityChecks = thisUrl.searchParams.has('opfs-sanity-check'); + options.sanityChecks = urlParams.has('opfs-sanity-check'); } if(undefined===options.proxyUri){ options.proxyUri = callee.defaultProxyUri; } - delete sqlite3.installOpfsVfs; - - /** - Generic utilities for working with OPFS. This will get filled out - by the Promise setup and, on success, installed as sqlite3.opfs. - */ - const opfsUtil = Object.create(null); const thePromise = new Promise(function(promiseResolve, promiseReject){ - const logPrefix = "OPFS syncer:"; - const warn = (...args)=>{ - if(options.verbose>1) console.warn(logPrefix,...args); + const loggers = { + 0:console.error.bind(console), + 1:console.warn.bind(console), + 2:console.log.bind(console) }; - if(self.window===self || - !self.SharedArrayBuffer || - !self.FileSystemHandle || - !self.FileSystemDirectoryHandle || - !self.FileSystemFileHandle || - !self.FileSystemFileHandle.prototype.createSyncAccessHandle || - !navigator.storage.getDirectory){ - warn("This environment does not have OPFS support."); - promiseReject(new Error("This environment does not have OPFS support.")); - return; - } + const logImpl = (level,...args)=>{ + if(options.verbose>level) loggers[level]("OPFS syncer:",...args); + }; + const log = (...args)=>logImpl(2, ...args); + const warn = (...args)=>logImpl(1, ...args); + const error = (...args)=>logImpl(0, ...args); warn("The OPFS VFS feature is very much experimental and under construction."); const toss = function(...args){throw new Error(args.join(' '))}; - const log = (...args)=>{ - if(options.verbose>2) console.log(logPrefix,...args); - }; - const error = (...args)=>{ - if(options.verbose>0) console.error(logPrefix,...args); - }; const capi = sqlite3.capi; const wasm = capi.wasm; const sqlite3_vfs = capi.sqlite3_vfs; @@ -129,9 +124,16 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) const W = new Worker(options.proxyUri); W._originalOnError = W.onerror /* will be restored later */; W.onerror = function(err){ + // The error object doesn't contain any useful info when the + // failure is, e.g., that the remote script is 404. promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons.")); }; const wMsg = (type,payload)=>W.postMessage({type,payload}); + /** + Generic utilities for working with OPFS. This will get filled out + by the Promise setup and, on success, installed as sqlite3.opfs. + */ + const opfsUtil = Object.create(null); /** State which we send to the async-api Worker or share with it. @@ -625,7 +627,8 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) log("xAccess(",dbFile,") exists ?=",rc); rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile, fid, openFlags, pOut); - log("open rc =",rc,"state.opSABView[xOpen] =",state.opSABView[state.opIds.xOpen]); + log("open rc =",rc,"state.opSABView[xOpen] =", + state.opSABView[state.opIds.xOpen]); if(isWorkerErrCode(rc)){ error("open failed with code",rc); return; @@ -696,8 +699,8 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) W.onerror = W._originalOnError; delete W._originalOnError; sqlite3.opfs = opfsUtil; - promiseResolve(sqlite3); log("End of OPFS sqlite3_vfs setup.", opfsVfs); + promiseResolve(sqlite3); }catch(e){ error(e); promiseReject(e); diff --git a/ext/wasm/sqlite3-opfs-async-proxy.js b/ext/wasm/sqlite3-opfs-async-proxy.js index 3ae6af043a..7eb12425cc 100644 --- a/ext/wasm/sqlite3-opfs-async-proxy.js +++ b/ext/wasm/sqlite3-opfs-async-proxy.js @@ -49,18 +49,20 @@ const state = Object.create(null); */ state.verbose = 2; -const __logPrefix = "OPFS asyncer:"; -const log = (...args)=>{ - if(state.verbose>2) console.log(__logPrefix,...args); +const loggers = { + 0:console.error.bind(console), + 1:console.warn.bind(console), + 2:console.log.bind(console) }; -const warn = (...args)=>{ - if(state.verbose>1) console.warn(__logPrefix,...args); -}; -const error = (...args)=>{ - if(state.verbose) console.error(__logPrefix,...args); +const logImpl = (level,...args)=>{ + if(state.verbose>level) loggers[level]("OPFS asyncer:",...args); }; +const log = (...args)=>logImpl(2, ...args); +const warn = (...args)=>logImpl(1, ...args); +const error = (...args)=>logImpl(0, ...args); -warn("This file is very much experimental and under construction.",self.location.pathname); +warn("This file is very much experimental and under construction.", + self.location.pathname); /** Map of sqlite3_file pointers (integers) to metadata related to a @@ -82,7 +84,7 @@ const getResolvedPath = function(filename,splitIt){ filename, 'file://irrelevant' ).pathname; return splitIt ? p.split('/').filter((v)=>!!v) : p; -} +}; /** Takes the absolute path to a filesystem element. Returns an array diff --git a/manifest b/manifest index c678bc0b2d..46fcb98d2f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Correct\sOPFS\sVFS\sxRead()\sto\scopy\sthe\sresult\sbuffer\sif\sthe\sresult\scode\sis\sSQLITE_IOERR_SHORT_READ. -D 2022-09-19T09:25:25.676 +C Generic\scleanups\sin\sthe\sOPFS\sVFS\sproxies. +D 2022-09-19T09:58:01.741 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -484,7 +484,7 @@ F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 F ext/wasm/api/sqlite3-api-oo1.js 2d13dddf0d2b4168a9249f124134d37924331e5b55e05dba18b6d661fbeefe48 -F ext/wasm/api/sqlite3-api-opfs.js 6a48568014dcadee22ff8eaa13deab573fbae362a71194efb6c1214d41d6b48d +F ext/wasm/api/sqlite3-api-opfs.js 580be306be7301fa0b3cb2abd5765561a3d7f4746a7679f95394af50a14671bb F ext/wasm/api/sqlite3-api-prologue.js 4e3e26880d444000cca1b4f3ddfa9d49581dfecd1de9426080239ecc208c447d F ext/wasm/api/sqlite3-api-worker1.js ee4cf149cbacb63d06b536674f822aa5088b7e022cdffc69f1f36cebe2f9fea0 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 @@ -521,7 +521,7 @@ F ext/wasm/speedtest1.html fbb8e4d1639028443f3687a683be660beca6927920545cf6b1fdf F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 -F ext/wasm/sqlite3-opfs-async-proxy.js 7806909f6957c6173843b53087b7f01fb41fb27f52bdad250fe2081d0fa44f9e +F ext/wasm/sqlite3-opfs-async-proxy.js 2e9a95fc6204f53ed871d04bdd0341d1c0d1a49c22d43b38144a5319f865930a F ext/wasm/sqlite3-worker1-promiser.js 4fd0465688a28a75f1d4ee4406540ba494f49844e3cad0670d0437a001943365 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/test-opfs-vfs.html eb69dda21eb414b8f5e3f7c1cc0f774103cc9c0f87b2d28a33419e778abfbab5 @@ -2028,8 +2028,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P b9773f164878b0a1b7c88cc7a6d1374ea95f64920065e8b2b178a1afffd82fe5 -R a6befd159a478cdd8d3c42193659027b +P 56668f9902c6e896b6c63621a444444c6f58ee20f88a5feae97f1699be35892d +R b87c8cb1e595f1f55de64a473bfd1ee8 U stephan -Z 2c8c2a7ce6b266c9bc7d1baf57207e7e +Z 85b747fc9dead0d23b6c33a91c074db8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e3f6dd88b5..f69e6b9a41 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -56668f9902c6e896b6c63621a444444c6f58ee20f88a5feae97f1699be35892d \ No newline at end of file +7d5f6adc3b964413fc96ad8d2735312c3e58348024cabdd2099682cbf696eaf7 \ No newline at end of file From ba0b157583c1977fe45f695c3bf640f7b1dcf682 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 19 Sep 2022 10:13:01 +0000 Subject: [PATCH 134/428] Minor doc improvements in demo-123.js. FossilOrigin-Name: 9370ce94780e974da03d5c74cf9a5983eed333e7b3bc54e605c5f30b3fa99287 --- ext/wasm/demo-123.js | 12 ++++++------ manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ext/wasm/demo-123.js b/ext/wasm/demo-123.js index 7a4843a4d9..c7dc4c769c 100644 --- a/ext/wasm/demo-123.js +++ b/ext/wasm/demo-123.js @@ -10,7 +10,7 @@ *********************************************************************** - A basic demonstration of the SQLite3 OO API. + A basic demonstration of the SQLite3 "OO#1" API. */ 'use strict'; (function(){ @@ -47,11 +47,11 @@ log("transient b =",db.filename); /** Never(!) rely on garbage collection to clean up DBs and - (especially) statements. Always wrap their lifetimes in - try/finally construct, as demonstrated below. By and large, - client code can avoid lifetime-related complications of - prepared statement objects by using the DB.exec() method for - SQL execution. + (especially) prepared statements. Always wrap their lifetimes + in a try/finally construct, as demonstrated below. By and + large, client code can entirely avoid lifetime-related + complications of prepared statement objects by using the + DB.exec() method for SQL execution. */ try { log("Create a table..."); diff --git a/manifest b/manifest index 46fcb98d2f..bbd2f4b6f4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Generic\scleanups\sin\sthe\sOPFS\sVFS\sproxies. -D 2022-09-19T09:58:01.741 +C Minor\sdoc\simprovements\sin\sdemo-123.js. +D 2022-09-19T10:13:01.255 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -498,7 +498,7 @@ F ext/wasm/common/testing.css 3a5143699c2b73a85b962271e1a9b3241b30d90e30d895e4f5 F ext/wasm/common/whwasmutil.js f7282ef36c9625330d4e6e82d1beec6678cd101e95e7108cd85db587a788c145 F ext/wasm/demo-123-worker.html e419b66495d209b5211ec64903b4cfb3ca7df20d652b41fcd28bf018a773234f F ext/wasm/demo-123.html aa281d33b7eefa755f3122b7b5a18f39a42dc5fb69c8879171bf14b4c37c4ec4 -F ext/wasm/demo-123.js 2c5e7775339624819067fd169d202004faeddc9d8ef452dd1f95aa4b3972a5b2 +F ext/wasm/demo-123.js 234655683e35a4543a23de7b10800d76b0369947b33e089e5613171fa7795afb F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 @@ -2028,8 +2028,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 56668f9902c6e896b6c63621a444444c6f58ee20f88a5feae97f1699be35892d -R b87c8cb1e595f1f55de64a473bfd1ee8 +P 7d5f6adc3b964413fc96ad8d2735312c3e58348024cabdd2099682cbf696eaf7 +R 2fec471b1001d01ad7dc38d9c67df837 U stephan -Z 85b747fc9dead0d23b6c33a91c074db8 +Z d026bdd80c48b40067e840d055db82e8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f69e6b9a41..e43adac6b2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7d5f6adc3b964413fc96ad8d2735312c3e58348024cabdd2099682cbf696eaf7 \ No newline at end of file +9370ce94780e974da03d5c74cf9a5983eed333e7b3bc54e605c5f30b3fa99287 \ No newline at end of file From b5ae85eca27f43949ae81a8489c3168ebcf7164c Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 19 Sep 2022 13:16:35 +0000 Subject: [PATCH 135/428] Merge kv-vfs branch into fiddle-opfs. Adjust various JS APIs and apps to deal with the new method of handling kvvfs. Adjust speedtest1 JS build to include sqlite3-api.js so that it can use kvvfs and opfs VFSes. Permit passing of the vfs as a URL parameter to certain demo/test apps. Milestone: speedtest-worker.html?vfs=opfs runs with the standalone OPFS impl. FossilOrigin-Name: ec09f32f7ae2249aaf27388ad2062982afa8bbbb5f88d236d6d9068bf33ad93d --- ext/wasm/GNUmakefile | 37 ++++--- ext/wasm/api/sqlite3-api-prologue.js | 2 + ext/wasm/{kvvfs1.html => demo-kvvfs1.html} | 4 +- ext/wasm/{kvvfs1.js => demo-kvvfs1.js} | 9 +- ext/wasm/index.html | 7 +- ext/wasm/kvvfs.make | 112 --------------------- ext/wasm/speedtest1-wasmfs.html | 2 +- ext/wasm/speedtest1-worker.html | 18 +++- ext/wasm/speedtest1-worker.js | 34 +++---- ext/wasm/speedtest1.html | 56 +++++++---- manifest | 31 +++--- manifest.uuid | 2 +- test/speedtest1.c | 11 +- 13 files changed, 132 insertions(+), 193 deletions(-) rename ext/wasm/{kvvfs1.html => demo-kvvfs1.html} (95%) rename ext/wasm/{kvvfs1.js => demo-kvvfs1.js} (95%) delete mode 100644 ext/wasm/kvvfs.make diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 730c31642b..e1b6784d38 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -53,7 +53,8 @@ SQLITE_OPT = \ -DSQLITE_OMIT_UTF16 \ -DSQLITE_OMIT_SHARED_CACHE \ -DSQLITE_THREADSAFE=0 \ - -DSQLITE_TEMP_STORE=3 + -DSQLITE_TEMP_STORE=3 \ + -DSQLITE_OS_KV_OPTIONAL=1 #SQLITE_OPT += -DSQLITE_ENABLE_MEMSYS5 # ^^^ MEMSYS5 is hypothetically useful for non-Emscripten builds but # requires adding more infrastructure and fixing one spot in the @@ -192,16 +193,19 @@ emcc.cflags += -I. -I$(dir.top) # $(SQLITE_OPT) ######################################################################## # emcc flags specific to building the final .js/.wasm file... emcc.jsflags := -fPIC -emcc.jsflags := --minify 0 +emcc.jsflags += --minify 0 emcc.jsflags += --no-entry emcc.jsflags += -sMODULARIZE emcc.jsflags += -sSTRICT_JS emcc.jsflags += -sDYNAMIC_EXECUTION=0 emcc.jsflags += -sNO_POLYFILL emcc.jsflags += -sEXPORTED_FUNCTIONS=@$(dir.wasm)/EXPORTED_FUNCTIONS.api -emcc.jsflags += -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory,allocateUTF8OnStack - # wasmMemory ==> required by our code for use with -sIMPORTED_MEMORY - # allocateUTF8OnStack => for kvvp internals +emcc.exportedRuntimeMethods := \ + -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory,allocateUTF8OnStack + # FS ==> stdio/POSIX I/O proxies + # wasmMemory ==> required by our code for use with -sIMPORTED_MEMORY + # allocateUTF8OnStack => for kvvfs internals +emcc.jsflags += $(emcc.exportedRuntimeMethods) emcc.jsflags += -sUSE_CLOSURE_COMPILER=0 emcc.jsflags += -sIMPORTED_MEMORY emcc.environment := -sENVIRONMENT=web @@ -332,17 +336,23 @@ speedtest1-common.eflags := -g $(emcc_opt) speedtest1.eflags := speedtest1.eflags += -sENVIRONMENT=web speedtest1-common.eflags += -sINVOKE_RUN=0 -#speedtest1-common.eflags += --no-entry -speedtest1-common.eflags += -flto +speedtest1-common.eflags += --no-entry +#speedtest1-common.eflags += -flto speedtest1-common.eflags += -sABORTING_MALLOC speedtest1-common.eflags += -sINITIAL_MEMORY=128450560 speedtest1-common.eflags += -sSTRICT_JS speedtest1-common.eflags += -sMODULARIZE speedtest1-common.eflags += -Wno-limited-postlink-optimizations -speedtest1-common.eflags += -sEXPORTED_FUNCTIONS=_main,_malloc,_free +speedtest1-common.eflags += -sEXPORTED_FUNCTIONS=@$(dir.wasm)/EXPORTED_FUNCTIONS.speedtest1 +speedtest1-common.eflags += $(emcc.exportedRuntimeMethods) +speedtest1-common.eflags += -sALLOW_TABLE_GROWTH speedtest1-common.eflags += -sDYNAMIC_EXECUTION=0 speedtest1-common.eflags += --minify 0 speedtest1-common.eflags += -sEXPORT_NAME=sqlite3Speedtest1InitModule +speedtest1-common.eflags += --post-js=$(post-js.js) +ifneq (0,$(enable_bigint)) +speedtest1-common.eflags += -sWASM_BIGINT +endif speedtest1.exit-runtime0 := -sEXIT_RUNTIME=0 speedtest1.exit-runtime1 := -sEXIT_RUNTIME=1 # Re -sEXIT_RUNTIME=1 vs 0: if it's 1 and speedtest1 crashes, we get @@ -362,25 +372,29 @@ speedtest1.exit-runtime1 := -sEXIT_RUNTIME=1 # -sEXIT_RUNTIME=1 but we need EXIT_RUNTIME=0 for the worker-based app # which runs speedtest1 multiple times. +EXPORTED_FUNCTIONS.speedtest1: EXPORTED_FUNCTIONS.api + { echo _wasm_main; cat EXPORTED_FUNCTIONS.api; } > $@ +CLEAN_FILES += EXPORTED_FUNCTIONS.speedtest1 speedtest1.js := speedtest1.js speedtest1.wasm := $(subst .js,.wasm,$(speedtest1.js)) speedtest1.cflags := \ -I. -I.. -I$(dir.top) \ -DSQLITE_SPEEDTEST1_WASM - +speedtest1.cs := $(speedtest1.c) $(sqlite3-wasm.c) $(jaccwabyt_test.c) $(speedtest1.js): emcc.cflags+= # speedtest1 notes re. sqlite3-wasm.o vs sqlite3-wasm.c: building against # the latter (predictably) results in a slightly faster binary, but we're # close enough to the target speed requirements that the 500ms makes a # difference. -$(speedtest1.js): $(speedtest1.c) $(sqlite3-wasm.c) $(MAKEFILE) $(sqlite3.c) +$(speedtest1.js): $(MAKEFILE) $(speedtest1.cs) $(post-js.js) \ + EXPORTED_FUNCTIONS.speedtest1 @echo "Building $@ ..." $(emcc.bin) \ $(speedtest1.eflags) $(speedtest1-common.eflags) $(speedtest1.cflags) \ $(SQLITE_OPT) \ $(speedtest1.exit-runtime0) \ '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' \ - -o $@ $(speedtest1.c) $(sqlite3-wasm.c) -lm + -o $@ $(speedtest1.cs) -lm $(maybe-wasm-strip) $(speedtest1.wasm) ls -la $@ $(speedtest1.wasm) @@ -434,7 +448,6 @@ oz: ######################################################################## # Sub-makes... -include kvvfs.make ######################################################################## # Some platforms do not support the WASMFS build. Raspberry Pi OS is one diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index add8ad658e..bd9779b7ef 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -749,6 +749,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( }; /** + Experimental and subject to change or removal. + Returns true if sqlite3.capi.sqlite3_web_persistent_dir() is a non-empty string and the given name starts with (that string + '/'), else returns false. diff --git a/ext/wasm/kvvfs1.html b/ext/wasm/demo-kvvfs1.html similarity index 95% rename from ext/wasm/kvvfs1.html rename to ext/wasm/demo-kvvfs1.html index 5d9f077958..caa3cdec85 100644 --- a/ext/wasm/kvvfs1.html +++ b/ext/wasm/demo-kvvfs1.html @@ -42,8 +42,8 @@ .toolbar > * { margin: 0.25em; } fieldset { border-radius: 0.5em; } - + - + diff --git a/ext/wasm/kvvfs1.js b/ext/wasm/demo-kvvfs1.js similarity index 95% rename from ext/wasm/kvvfs1.js rename to ext/wasm/demo-kvvfs1.js index 57f990f16b..a2cff8639d 100644 --- a/ext/wasm/kvvfs1.js +++ b/ext/wasm/demo-kvvfs1.js @@ -10,8 +10,9 @@ *********************************************************************** - A basic test script for sqlite3-kvvfs.wasm. This file must be run in - main JS thread and sqlite3-kvvfs.js must have been loaded before it. + A basic test script for sqlite3.wasm with kvvfs support. This file + must be run in main JS thread and sqlite3.js must have been loaded + before it. */ 'use strict'; (function(){ @@ -39,7 +40,7 @@ const error = function(...args){ logHtml('error',...args); }; - + const runTests = function(Module){ //log("Module",Module); const sqlite3 = Module.sqlite3, @@ -49,7 +50,7 @@ log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); T.assert( 0 !== capi.sqlite3_vfs_find(null) ); if(!capi.sqlite3_vfs_find('kvvfs')){ - warn("This build is not kvvfs-capable."); + error("This build is not kvvfs-capable."); return; } diff --git a/ext/wasm/index.html b/ext/wasm/index.html index 68b43d7652..06dd0f31e4 100644 --- a/ext/wasm/index.html +++ b/ext/wasm/index.html @@ -51,9 +51,10 @@
    • speedtest1: a main-thread WASM build of speedtest1.
    • speedtest1-worker: an interactive Worker-thread variant of speedtest1.
    • speedtest1-wasmfs: a variant of speedtest1 built solely for the wasmfs/opfs feature.
    • -
    • speedtest1-kvvfs: a variant of speedtest1 built solely for the kv-vfs feature.
    • -
    • kvvfs1: very basic demo of using the key-value vfs for storing - a persistent db in JS localStorage or sessionStorage.
    • +
    • speedtest1-kvvfs: speedtest1 with the kvvfs.
    • +
    • demo-kvvfs1: very basic + demo of using the key-value vfs for storing a persistent db + in JS localStorage or sessionStorage.
    • scratchpad-wasmfs-main: experimenting with WASMFS/OPFS-based persistence. Maintenance reminder: we cannot currently (2022-09-15) load WASMFS in a diff --git a/ext/wasm/kvvfs.make b/ext/wasm/kvvfs.make deleted file mode 100644 index d9a9539566..0000000000 --- a/ext/wasm/kvvfs.make +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/make -#^^^^ help emacs select makefile mode -# -# This is a sub-make for building a standalone kvvfs-based -# sqlite3.wasm. It is intended to be "include"d from the main -# GNUMakefile. -# -# Notable potential TODOs: -# -# - Trim down a custom sqlite3-api.js for this build. We can elimate -# the jaccwabyt dependency, for example, because this build won't -# make use of the VFS bits. Similarly, we can eliminate or replace -# parts of the OO1 API, or provide a related API which manages -# singletons of the localStorage/sessionStorage instances. -# -######################################################################## -MAKEFILE.kvvfs := $(lastword $(MAKEFILE_LIST)) - -kvvfs.js := sqlite3-kvvfs.js -kvvfs.wasm := sqlite3-kvvfs.wasm -kvvfs.wasm.c := $(dir.api)/sqlite3-wasm.c - -CLEAN_FILES += $(kvvfs.js) $(kvvfs.wasm) - -######################################################################## -# emcc flags for .c/.o/.wasm. -kvvfs.flags = -#kvvfs.flags += -v # _very_ loud but also informative about what it's doing - -######################################################################## -# emcc flags for .c/.o. -kvvfs.cflags := -kvvfs.cflags += -std=c99 -fPIC -g -kvvfs.cflags += -I. -I$(dir.top) -kvvfs.cflags += -DSQLITE_OS_KV=1 $(SQLITE_OPT) - -kvvfs.extra.c := -ifeq (1,1) - # To get testing1.js to run with $(kvvfs.js) we need... - kvvfs.extra.c += $(jaccwabyt_test.c) -endif - -######################################################################## -# emcc flags specific to building the final .js/.wasm file... -kvvfs.jsflags := -fPIC -kvvfs.jsflags += --no-entry -kvvfs.jsflags += --minify 0 -kvvfs.jsflags += -sENVIRONMENT=web -kvvfs.jsflags += -sMODULARIZE -kvvfs.jsflags += -sSTRICT_JS -kvvfs.jsflags += -sDYNAMIC_EXECUTION=0 -kvvfs.jsflags += -sNO_POLYFILL -ifeq (,$(kvvfs.extra.c)) - kvvfs.jsflags += -sEXPORTED_FUNCTIONS=@$(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api -else - # need more exports for jaccwabyt test code... - kvvfs.jsflags += -sEXPORTED_FUNCTIONS=@$(dir.wasm)/EXPORTED_FUNCTIONS.api -endif -kvvfs.jsflags += -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory,allocateUTF8OnStack - # wasmMemory ==> for -sIMPORTED_MEMORY - # allocateUTF8OnStack ==> kvvfs internals -kvvfs.jsflags += -sUSE_CLOSURE_COMPILER=0 -kvvfs.jsflags += -sIMPORTED_MEMORY -#kvvfs.jsflags += -sINITIAL_MEMORY=13107200 -#kvvfs.jsflags += -sTOTAL_STACK=4194304 -kvvfs.jsflags += -sEXPORT_NAME=sqlite3InitModule -kvvfs.jsflags += -sGLOBAL_BASE=4096 # HYPOTHETICALLY keep func table indexes from overlapping w/ heap addr. -kvvfs.jsflags += --post-js=$(post-js.js) -#kvvfs.jsflags += -sFILESYSTEM=0 # only for experimentation. sqlite3 needs the FS API -# Perhaps the kvvfs build doesn't? -#kvvfs.jsflags += -sABORTING_MALLOC -kvvfs.jsflags += -sALLOW_MEMORY_GROWTH -kvvfs.jsflags += -sALLOW_TABLE_GROWTH -kvvfs.jsflags += -Wno-limited-postlink-optimizations -# ^^^^^ it likes to warn when we have "limited optimizations" via the -g3 flag. -kvvfs.jsflags += -sERROR_ON_UNDEFINED_SYMBOLS=0 -kvvfs.jsflags += -sLLD_REPORT_UNDEFINED -#kvvfs.jsflags += --import-undefined -kvvfs.jsflags += -sMEMORY64=0 -ifneq (0,$(enable_bigint)) -kvvfs.jsflags += -sWASM_BIGINT -endif -$(kvvfs.js): $(kvvfs.wasm.c) $(sqlite3.c) $(kvvfs.extra.c) \ - EXPORTED_FUNCTIONS.api $(MAKEFILE) $(MAKEFILE.kvvfs) \ - $(post-js.js) - @echo "Building $@ ..." - $(emcc.bin) -o $@ $(emcc_opt) $(emcc.flags) \ - $(SQLITE_OPT) \ - $(kvvfs.cflags) $(kvvfs.jsflags) $(kvvfs.wasm.c) $(kvvfs.extra.c) - chmod -x $(kvvfs.wasm) - $(maybe-wasm-strip) $(kvvfs.wasm) - @ls -la $@ $(kvvfs.wasm) - -kvvfs: $(kvvfs.js) -all: kvvfs - -######################################################################## -# speedtest1-kvvfs -speedtest1-kvvfs.js := speedtest1-kvvfs.js -speedtest1-kvvfs.wasm := speedtest1-kvvfs.wasm -CLEAN_FILES += $(speedtest1-kvvfs.js) $(speedtest1-kvvfs.wasm) -$(speedtest1-kvvfs.js): $(speedtest1.c) $(sqlite3-wasm.c) $(sqlite3.c) $(MAKEFILE.kvvfs) - $(emcc.bin) \ - $(speedtest1.eflags) $(speedtest1-common.eflags) $(speedtest1.cflags) \ - $(SQLITE_OPT) \ - $(speedtest1.exit-runtime1) \ - $(kvvfs.cflags) \ - -o $@ $(speedtest1.c) $(sqlite3-wasm.c) -lm - $(maybe-wasm-strip) $(speedtest1-kvvfs.wasm) - ls -la $@ $(speedtest1-kvvfs.wasm) - -speedtest1: $(speedtest1-kvvfs.js) diff --git a/ext/wasm/speedtest1-wasmfs.html b/ext/wasm/speedtest1-wasmfs.html index 151ced89a3..c8e93f6dcf 100644 --- a/ext/wasm/speedtest1-wasmfs.html +++ b/ext/wasm/speedtest1-wasmfs.html @@ -139,7 +139,7 @@ "Give it time..."); logList.length = 0; setTimeout(function(){ - wasm.xCall('__main_argc_argv', argv.length, + wasm.xCall('wasm_main', argv.length, wasm.scopedAllocMainArgv(argv)); wasm.scopedAllocPop(scope); if(pDir) unlink(dbFile); diff --git a/ext/wasm/speedtest1-worker.html b/ext/wasm/speedtest1-worker.html index 3c9740286f..bc3f10a6fc 100644 --- a/ext/wasm/speedtest1-worker.html +++ b/ext/wasm/speedtest1-worker.html @@ -30,7 +30,9 @@
      -
      TODO? Options which require values are not represented here.
      +
      The following flags can be passed as URL parameters: + vfs=NAME, size=N +
      @@ -124,6 +126,7 @@ justify-content: flex-start; } + diff --git a/manifest b/manifest index 5e90405e4e..16af6e39d6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C speedtest1:\sadd\s--vfs\sNAME\sflag\sand\seliminate\scode\sduplication\sin\sargc\ssize\sverification\sfor\sall\sflags\swhich\stake\sa\svalue. -D 2022-09-19T11:47:38.781 +C Merge\skv-vfs\sbranch\sinto\sfiddle-opfs.\sAdjust\svarious\sJS\sAPIs\sand\sapps\sto\sdeal\swith\sthe\snew\smethod\sof\shandling\skvvfs.\sAdjust\sspeedtest1\sJS\sbuild\sto\sinclude\ssqlite3-api.js\sso\sthat\sit\scan\suse\skvvfs\sand\sopfs\sVFSes.\sPermit\spassing\sof\sthe\svfs\sas\sa\sURL\sparameter\sto\scertain\sdemo/test\sapps.\sMilestone:\sspeedtest-worker.html?vfs=opfs\sruns\swith\sthe\sstandalone\sOPFS\simpl. +D 2022-09-19T13:16:35.953 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 19ba3304cf4e5e5f480a5c3701b2c7d7b7383f70416359fc4a1d304b4ffdd895 +F ext/wasm/GNUmakefile b6a5b642e8b3e587d3edcfeb6b6275acbe4730293f4ad46c4997cd932d57aec5 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 150a793a47205b8009ac934f3b6d6ebf67b965c072339aaa25ce808a19e116cc F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -485,7 +485,7 @@ F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 F ext/wasm/api/sqlite3-api-oo1.js 2d13dddf0d2b4168a9249f124134d37924331e5b55e05dba18b6d661fbeefe48 F ext/wasm/api/sqlite3-api-opfs.js 580be306be7301fa0b3cb2abd5765561a3d7f4746a7679f95394af50a14671bb -F ext/wasm/api/sqlite3-api-prologue.js 4e3e26880d444000cca1b4f3ddfa9d49581dfecd1de9426080239ecc208c447d +F ext/wasm/api/sqlite3-api-prologue.js 7b1e4a45f733a6f95551b383eb37cb86754d18214b11f668298f7a83a23ef732 F ext/wasm/api/sqlite3-api-worker1.js ee4cf149cbacb63d06b536674f822aa5088b7e022cdffc69f1f36cebe2f9fea0 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 4130e2df9587f4e4c3afc04c3549d682c8a5c0cfe5b22819a0a86edb7f01b9bd @@ -499,25 +499,24 @@ F ext/wasm/common/whwasmutil.js f7282ef36c9625330d4e6e82d1beec6678cd101e95e7108c F ext/wasm/demo-123-worker.html e419b66495d209b5211ec64903b4cfb3ca7df20d652b41fcd28bf018a773234f F ext/wasm/demo-123.html aa281d33b7eefa755f3122b7b5a18f39a42dc5fb69c8879171bf14b4c37c4ec4 F ext/wasm/demo-123.js 234655683e35a4543a23de7b10800d76b0369947b33e089e5613171fa7795afb +F ext/wasm/demo-kvvfs1.html 7d4f28873de67f51ac18c584b7d920825139866a96049a49c424d6f5a0ea5e7f w ext/wasm/kvvfs1.html +F ext/wasm/demo-kvvfs1.js e884ea35022d772c0d1dd884b40011413696438394f605c6cd4808cfb1642a4a w ext/wasm/kvvfs1.js F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 -F ext/wasm/index.html aed40adf52598a353e27e50480d53e59a5e75e9ba889f7d8bb2fda45a0a91c3b +F ext/wasm/index.html de69bc78273fbe024a21dae0f378a87b37ad4cd585ed0cd0a0ed2c5d5afe9fbc F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19 -F ext/wasm/kvvfs.make 4b2ba6d061f3a52da9f5812f86f4faa80fb4d9456a152f6b0585dccd667a4e22 -F ext/wasm/kvvfs1.html 13bb24190bfb276a57b228499519badcc1bf39ed07e4b37bc2a425ce6418fed1 -F ext/wasm/kvvfs1.js ec1c1d071bb055711f9151df05616111432cf3e6bf7ac7f8dcbcfb56c9d9ed48 F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06 F ext/wasm/scratchpad-wasmfs-main.js f0836e3576df7a89390d777bb53e142e559e8a79becfb2a5a976490b05a1c4fa F ext/wasm/speedtest1-kvvfs.html c8b65c20e2b35298dc02d8e0a394d5e1eb857fd22e504468388234aee13aef08 -F ext/wasm/speedtest1-wasmfs.html 6a67a6812f03a2058eb5c6ad0c8dea4bf749d0160ed9d6b826dabe7b766c3cf7 -F ext/wasm/speedtest1-worker.html d8881ae802d15fb8adb94049265173e99f350e07e1d4e6f9e1cbd8969fe63a04 -F ext/wasm/speedtest1-worker.js fb5d282c0b8aed18daf41c57f768cbf434f8137dbff707d53dcedcd7d4cb60ef -F ext/wasm/speedtest1.html fbb8e4d1639028443f3687a683be660beca6927920545cf6b1fdf503104591c0 +F ext/wasm/speedtest1-wasmfs.html 3a6f89fdd025d137ea9122a84a454d5d31cafa8495e7371e984f4d4b8ce58834 +F ext/wasm/speedtest1-worker.html edbfbff9249b33a889fb76e7df9640d83e03f1e0d25b6c9e228f00d629a76ed0 +F ext/wasm/speedtest1-worker.js d26605f9518978ccbefbec2cbcdf90437c00828e3cb0fd69712f58b37f9e071c +F ext/wasm/speedtest1.html 225ef377f7c42b8044505d9ef1a8c97c8dddb6d8799d322bd654c9f70c0f2f6f F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 @@ -1508,7 +1507,7 @@ F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715 F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d1f0c -F test/speedtest1.c 4be5c905c7b3ee72d8e54dea3a846b8c3e7f9674cf3d080968e0e6651ba3c302 +F test/speedtest1.c 752fc1cad512f7c94271d3fdb46d6d90c9f6b61d0a4722a28c7028f53952fb1d F test/spellfix.test 951a6405d49d1a23d6b78027d3877b4a33eeb8221dcab5704b499755bb4f552e F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3 F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33 @@ -2028,8 +2027,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 9370ce94780e974da03d5c74cf9a5983eed333e7b3bc54e605c5f30b3fa99287 354726aa6c399053785f29104de15091629ce4bc275b9e2205cb3656a9e81cd7 -R 00556834ec19d453fd785f2bebf86235 +P 281d09867134e0a057cfadd9bfcbf0e21b8ac9737a278c41973d33a3101be7bc +R 6442d22e3df1d96228caf48bf4bfd7a0 U stephan -Z 19b45de03e5674211a91148844c56c0b +Z 5bc762c205d5862d76e828c3121841fc # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 582e7ce8e3..d3459cc1a5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -281d09867134e0a057cfadd9bfcbf0e21b8ac9737a278c41973d33a3101be7bc \ No newline at end of file +ec09f32f7ae2249aaf27388ad2062982afa8bbbb5f88d236d6d9068bf33ad93d \ No newline at end of file diff --git a/test/speedtest1.c b/test/speedtest1.c index d686c49663..d1efdf13a0 100644 --- a/test/speedtest1.c +++ b/test/speedtest1.c @@ -2197,7 +2197,6 @@ static int xCompileOptions(void *pCtx, int nVal, char **azVal, char **azCol){ printf("-- Compile option: %s\n", azVal[0]); return SQLITE_OK; } - int main(int argc, char **argv){ int doAutovac = 0; /* True for --autovacuum */ int cacheSize = 0; /* Desired cache size. 0 means default */ @@ -2634,3 +2633,13 @@ int main(int argc, char **argv){ free( pHeap ); return 0; } + +#ifdef SQLITE_SPEEDTEST1_WASM +/* +** A workaround for some inconsistent behaviour with how +** main() does (or does not) get exported to WASM. +*/ +int wasm_main(int argc, char **argv){ + return main(argc, argv); +} +#endif From 0e0687ccfc2c898489cdc7ca8116b2dd2a3bcb54 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 19 Sep 2022 13:44:23 +0000 Subject: [PATCH 136/428] Add sqlite3_web_vfs_list() to JS API. Corrected OPFS VFS's registering itself as the default VFS. speedtest1-worker now uses the xDelete() of both the default VFS and OPFS, to avoid that it starts up with a persistent OPFS test db (the native app calls unlink(), but that unlink call operates on a different virtual filesystem than the OPFS VFS). FossilOrigin-Name: 2ec7e09139a510b9fd29e4c97283b20740a00f369193c6fecbb734f187e81b48 --- ext/wasm/api/sqlite3-api-opfs.js | 6 +++--- ext/wasm/api/sqlite3-api-prologue.js | 16 ++++++++++++++++ ext/wasm/speedtest1-wasmfs.html | 3 +-- ext/wasm/speedtest1-worker.html | 3 +-- ext/wasm/speedtest1-worker.js | 10 ++++++++-- ext/wasm/speedtest1.html | 3 +-- manifest | 26 +++++++++++++------------- manifest.uuid | 2 +- 8 files changed, 44 insertions(+), 25 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-opfs.js b/ext/wasm/api/sqlite3-api-opfs.js index e0554e8c0c..62aad0c4bb 100644 --- a/ext/wasm/api/sqlite3-api-opfs.js +++ b/ext/wasm/api/sqlite3-api-opfs.js @@ -560,13 +560,13 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) Returns true if the deletion succeeded and fails if it fails, but cannot report the nature of the failure. */ - opfsUtil.deleteEntry = function(fsEntryName,recursive){ + opfsUtil.deleteEntry = function(fsEntryName,recursive=false){ return 0===opRun('xDelete', {filename:fsEntryName, recursive}); }; /** Exactly like deleteEntry() but runs asynchronously. */ - opfsUtil.deleteEntryAsync = async function(fsEntryName,recursive){ + opfsUtil.deleteEntryAsync = async function(fsEntryName,recursive=false){ wMsg('xDeleteNoWait', {filename: fsEntryName, recursive}); }; /** @@ -682,7 +682,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) so we now know that the state object is no longer subject to being copied by a pending postMessage() call.*/ try { - const rc = capi.sqlite3_vfs_register(opfsVfs.pointer, opfsVfs.$zName); + const rc = capi.sqlite3_vfs_register(opfsVfs.pointer, 0); if(rc){ opfsVfs.dispose(); toss("sqlite3_vfs_register(OPFS) failed with rc",rc); diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index bd9779b7ef..7bc42b3683 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -813,6 +813,22 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( } }; + /** + Returns an array of the names of all currently-registered sqlite3 + VFSes. + */ + capi.sqlite3_web_vfs_list = function(){ + const rc = []; + let pVfs = capi.sqlite3_vfs_find(0); + while(pVfs){ + const oVfs = new capi.sqlite3_vfs(pVfs); + rc.push(capi.wasm.cstringToJs(oVfs.$zName)); + pVfs = oVfs.$pNext; + oVfs.dispose(); + } + return rc; + }; + if( self.window===self ){ /* Features specific to the main window thread... */ diff --git a/ext/wasm/speedtest1-wasmfs.html b/ext/wasm/speedtest1-wasmfs.html index c8e93f6dcf..d0400f7e76 100644 --- a/ext/wasm/speedtest1-wasmfs.html +++ b/ext/wasm/speedtest1-wasmfs.html @@ -114,8 +114,7 @@ const urlArgs = self.SqliteTestUtil.processUrlArgs(); const argv = ["speedtest1"]; if(urlArgs.flags){ - // transform flags=a,b,c to ["--a", "--b", "--c"] - argv.push(...(urlArgs.flags.split(',').map((v)=>'--'+v))); + argv.push(...(urlArgs.flags.split(','))); }else{ argv.push( "--singlethread", diff --git a/ext/wasm/speedtest1-worker.html b/ext/wasm/speedtest1-worker.html index bc3f10a6fc..38eff5cb27 100644 --- a/ext/wasm/speedtest1-worker.html +++ b/ext/wasm/speedtest1-worker.html @@ -126,7 +126,6 @@ justify-content: flex-start; } - - - - - - diff --git a/ext/wasm/speedtest1-wasmfs.html b/ext/wasm/speedtest1-wasmfs.html index d0400f7e76..37155282cd 100644 --- a/ext/wasm/speedtest1-wasmfs.html +++ b/ext/wasm/speedtest1-wasmfs.html @@ -36,7 +36,6 @@
      Output is delayed/buffered because we cannot update the UI while the speedtest is running. Output will appear below when ready...
      - diff --git a/ext/wasm/speedtest1-worker.html b/ext/wasm/speedtest1-worker.html index 38eff5cb27..0884c7429c 100644 --- a/ext/wasm/speedtest1-worker.html +++ b/ext/wasm/speedtest1-worker.html @@ -147,11 +147,11 @@ log2('', ...args); }; const logErr = function(...args){ - //console.error(...args); + console.error(...args); log2('error', ...args); }; const logWarn = function(...args){ - //console.warn(...args); + console.warn(...args); log2('warning', ...args); }; diff --git a/ext/wasm/speedtest1.html b/ext/wasm/speedtest1.html index be0be3d83f..275ba7de49 100644 --- a/ext/wasm/speedtest1.html +++ b/ext/wasm/speedtest1.html @@ -36,7 +36,6 @@
      Output is delayed/buffered because we cannot update the UI while the speedtest is running. Output will appear below when ready...
      - - - - - diff --git a/ext/wasm/index.html b/ext/wasm/index.html index 06dd0f31e4..e9a8d7bebb 100644 --- a/ext/wasm/index.html +++ b/ext/wasm/index.html @@ -47,7 +47,6 @@
    • testing-worker1-promiser: tests for the Promise-based wrapper of the Worker-based API.
    • batch-runner: runs batches of SQL exported from speedtest1.
    • -
    • batch-runner-kvvfs: KVVFS-specific variant of batch-runner.html.
    • speedtest1: a main-thread WASM build of speedtest1.
    • speedtest1-worker: an interactive Worker-thread variant of speedtest1.
    • speedtest1-wasmfs: a variant of speedtest1 built solely for the wasmfs/opfs feature.
    • diff --git a/manifest b/manifest index d5e6c8b22a..a6a7d0a7be 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Speed\sup\sde/serialization\sof\sfunc\sargs\sand\sreturn\svalues\sin\sthe\sOPFS\sVFS\sproxy. -D 2022-09-20T13:25:39.815 +C Remove\san\sobsolete\sfile. +D 2022-09-20T14:21:58.678 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 5b848d4d20c31b2aac3dcfabaf5deaf56fe9db9ca1299a4dede1bcd27821426b +F ext/wasm/GNUmakefile e6359a72f044a877ac6ca8f2b04fa643664157f84d81a6d9ad09f384a65160d2 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 150a793a47205b8009ac934f3b6d6ebf67b965c072339aaa25ce808a19e116cc F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -489,7 +489,6 @@ F ext/wasm/api/sqlite3-api-prologue.js 0d2639387b94c30f492d4aea6e44fb7b167208086 F ext/wasm/api/sqlite3-api-worker1.js ee4cf149cbacb63d06b536674f822aa5088b7e022cdffc69f1f36cebe2f9fea0 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 4130e2df9587f4e4c3afc04c3549d682c8a5c0cfe5b22819a0a86edb7f01b9bd -F ext/wasm/batch-runner-kvvfs.html ef3b2f553abad4f17a2a29ce6526023793a88e8597b7a24b8c7855a030b90a16 F ext/wasm/batch-runner.html 2857a6db7292ac83d1581af865d643fd34235db2df830d10b43b01388c599e04 F ext/wasm/batch-runner.js 6f5b86e0b5519a9a941d9f17ee9c5ecdc63f452f157602fe7fdf87f6275a2b49 F ext/wasm/common/SqliteTestUtil.js 529161a624265ba84271a52db58da022649832fa1c71309fb1e02cc037327a2b @@ -505,7 +504,7 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 -F ext/wasm/index.html de69bc78273fbe024a21dae0f378a87b37ad4cd585ed0cd0a0ed2c5d5afe9fbc +F ext/wasm/index.html 4b74ea334f0bba9923a61246d807a1873784fcef4d3fb342b698613d0a29fd9c F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f @@ -2026,8 +2025,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P b534831f3efb8910a17e29956e3e87cc80055ea66e15dbef992b6a556ff042f8 -R 28e069ac7c71e059841784989b21e62c +P 5bf235bbe035e4ace7a54851e190742528af6b4266328a1b8bbb9fb3dd7f2118 +R 41b4a0b0d5548be3b8c342b39c8cce92 U stephan -Z 483126717a4194dd1997ada5cd19f259 +Z 524d6845dd279f2ce03cb802d7ae16c8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 44047c5b0f..a0a90cad10 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5bf235bbe035e4ace7a54851e190742528af6b4266328a1b8bbb9fb3dd7f2118 \ No newline at end of file +1e43855498b4329f733bd6e0731841dc884e885de7d2d338d402d05d54309427 \ No newline at end of file From c3b6fdaead863709e532832c1341f1bebce504f2 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 20 Sep 2022 14:36:53 +0000 Subject: [PATCH 147/428] When compiled with SQLITE_OS_KV_OPTIONAL, the magic names ":localStorage:" and ":sessionStorage:" are recognized and converted to use the kv-vfs. FossilOrigin-Name: c5db9262d0388ccb0e84c6a4b4e2e786dd634f13874e4034ba7b175befa4ce90 --- manifest | 15 +++++++++------ manifest.uuid | 2 +- src/main.c | 13 +++++++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index d9677a2ca9..2851e8ad5b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\suninitialized\svariable\sin\sthe\sdecoder\skv-vfs. -D 2022-09-17T18:31:31.300 +C When\scompiled\swith\sSQLITE_OS_KV_OPTIONAL,\sthe\smagic\snames\s":localStorage:"\nand\s":sessionStorage:"\sare\srecognized\sand\sconverted\sto\suse\sthe\skv-vfs. +D 2022-09-20T14:36:53.844 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -555,7 +555,7 @@ F src/insert.c aea5361767817f917b0f0f647a1f0b1621bd858938ae6ae545c3b6b9814b798f F src/json.c 7749b98c62f691697c7ee536b570c744c0583cab4a89200fdd0fc2aa8cc8cbd6 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 853385cc7a604157e137585097949252d5d0c731768e16b044608e5c95c3614b -F src/main.c e11267cdd380be68d95d4292666636a7f1dff5f17a395f3d55be910d7e9350fb +F src/main.c b434b8e4aca38419962de3d1c55fb9279807ba6a8802998a427b1635dc8250f6 F src/malloc.c b7a3430cbe91d3e8e04fc10c2041b3a19794e63556ad2441a13d8dadd9b2bafc F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de @@ -2004,8 +2004,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 852812d1e2ec3c53ad7c6c64662b37d861fefcf1baeee3d58eba88bcb3f6d8df -R 9c6620d4dc4ed0d3ddc9cc9e79cbabf7 +P 354726aa6c399053785f29104de15091629ce4bc275b9e2205cb3656a9e81cd7 +R 2a2a6e9f98f7c53c15ad912991ea899c +T *branch * kv-vfs-magic-names +T *sym-kv-vfs-magic-names * +T -sym-kv-vfs * U drh -Z 7ef728d647d25bb1b8240f0a67ed5ff5 +Z 6a8bdecbcdd0ef58e91af5aa4ad5d7c2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1336748789..66706b769e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -354726aa6c399053785f29104de15091629ce4bc275b9e2205cb3656a9e81cd7 \ No newline at end of file +c5db9262d0388ccb0e84c6a4b4e2e786dd634f13874e4034ba7b175befa4ce90 \ No newline at end of file diff --git a/src/main.c b/src/main.c index efa5fc3681..e73fe351ca 100644 --- a/src/main.c +++ b/src/main.c @@ -3352,6 +3352,19 @@ static int openDatabase( goto opendb_out; } +#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) + /* Process magic filenames ":localStorage:" and ":sessionStorage:" */ + if( zFilename && zFilename[0]==':' ){ + if( strcmp(zFilename, ":localStorage:")==0 ){ + zFilename = "file:local?vfs=kvvfs"; + flags |= SQLITE_OPEN_URI; + }else if( strcmp(zFilename, ":sessionStorage:")==0 ){ + zFilename = "file:session?vfs=kvvfs"; + flags |= SQLITE_OPEN_URI; + } + } +#endif /* SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) */ + /* Parse the filename/URI argument ** ** Only allow sensible combinations of bits in the flags argument. From 89071030db862b7b9b26f214c3508e607b9b430d Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 20 Sep 2022 14:52:26 +0000 Subject: [PATCH 148/428] Export the sqlite3_uri_...() family of functions to wasm. FossilOrigin-Name: 72bebc848fce5c3b4766017d016ccb2360de2bd0cb3e47e710c80dbcb6b8b707 --- ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api | 4 ++++ ext/wasm/api/sqlite3-api-prologue.js | 12 ++++++++---- manifest | 15 +++++++-------- manifest.uuid | 2 +- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api index f03478b17c..3119d50d03 100644 --- a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api +++ b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api @@ -61,6 +61,10 @@ _sqlite3_strglob _sqlite3_strlike _sqlite3_total_changes _sqlite3_total_changes64 +_sqlite3_uri_boolean +_sqlite3_uri_int64 +_sqlite3_uri_key +_sqlite3_uri_parameter _sqlite3_value_blob _sqlite3_value_bytes _sqlite3_value_double diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 7bc42b3683..a671080052 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -649,6 +649,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( ["sqlite3_strglob", "int", "string","string"], ["sqlite3_strlike", "int", "string","string","int"], ["sqlite3_total_changes", "int", "sqlite3*"], + ["sqlite3_uri_boolean", "int", "string", "string", "int"], + ["sqlite3_uri_key", "string", "string", "int"], + ["sqlite3_uri_parameter", "string", "string", "string"], ["sqlite3_value_blob", "*", "*"], ["sqlite3_value_bytes","int", "*"], ["sqlite3_value_double","f64", "*"], @@ -671,10 +674,11 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( dummy impls, depending on the capabilities of the environment. */ capi.wasm.bindingSignatures.int64 = [ - ["sqlite3_bind_int64","int", ["sqlite3_stmt*", "int", "i64"]], - ["sqlite3_changes64","i64", ["sqlite3*"]], - ["sqlite3_column_int64","i64", ["sqlite3_stmt*", "int"]], - ["sqlite3_total_changes64", "i64", ["sqlite3*"]] + ["sqlite3_bind_int64","int", ["sqlite3_stmt*", "int", "i64"]], + ["sqlite3_changes64","i64", ["sqlite3*"]], + ["sqlite3_column_int64","i64", ["sqlite3_stmt*", "int"]], + ["sqlite3_total_changes64", "i64", ["sqlite3*"]], + ["sqlite3_uri_int64", "i64", ["string", "string", "i64"]] ]; /** diff --git a/manifest b/manifest index 15ba2f0999..8c30d17726 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sopfs-proxy-atomics\sbranch\sinto\sthe\sfiddle-opfs\sbranch. -D 2022-09-20T14:39:54.222 +C Export\sthe\ssqlite3_uri_...()\sfamily\sof\sfunctions\sto\swasm. +D 2022-09-20T14:52:26.772 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -476,7 +476,7 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle 7fb73f7150ab79d83bb45a67d257553c905c78cd3d6 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 F ext/wasm/GNUmakefile e6359a72f044a877ac6ca8f2b04fa643664157f84d81a6d9ad09f384a65160d2 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 -F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 150a793a47205b8009ac934f3b6d6ebf67b965c072339aaa25ce808a19e116cc +F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 8a724a674bd2089eef9676b434c0ab709da00db33f73a94e4987e90169b1cd14 F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 F ext/wasm/api/README.md d876597edd2b9542b6ea031adaaff1c042076fde7b670b1dc6d8a87b28a6631b F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c @@ -485,7 +485,7 @@ F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 F ext/wasm/api/sqlite3-api-oo1.js 2d13dddf0d2b4168a9249f124134d37924331e5b55e05dba18b6d661fbeefe48 F ext/wasm/api/sqlite3-api-opfs.js ce75aba0cbfb600cf839362012d17b7b2984aeac5189586c9a5a8f37a573a929 -F ext/wasm/api/sqlite3-api-prologue.js 0d2639387b94c30f492d4aea6e44fb7b16720808678464559458fd2ae3759655 +F ext/wasm/api/sqlite3-api-prologue.js 6f3a67c4db37e884d33a05e5cf6d9d9bc012226a18c09f33f662fefd99840a63 F ext/wasm/api/sqlite3-api-worker1.js ee4cf149cbacb63d06b536674f822aa5088b7e022cdffc69f1f36cebe2f9fea0 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 4130e2df9587f4e4c3afc04c3549d682c8a5c0cfe5b22819a0a86edb7f01b9bd @@ -2025,9 +2025,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 5ca412ced24b4e3af5f467e710a597ed440badf7b8335346aade11d3cad3d1a1 1e43855498b4329f733bd6e0731841dc884e885de7d2d338d402d05d54309427 -R 41b4a0b0d5548be3b8c342b39c8cce92 -T +closed 1e43855498b4329f733bd6e0731841dc884e885de7d2d338d402d05d54309427 Closed\sby\sintegrate-merge. +P 25a36920d44544547a84161681cd41e292b4a70df60ac3630791873a79237d98 +R 6ffa39d63e3e386cd82927e67533dcb0 U stephan -Z 919304ebee6398ab381c09fc060e29a5 +Z 98694450e0cd5d4ad65ed4d27ad0649d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 49a16c4ea6..9a6bcff385 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -25a36920d44544547a84161681cd41e292b4a70df60ac3630791873a79237d98 \ No newline at end of file +72bebc848fce5c3b4766017d016ccb2360de2bd0cb3e47e710c80dbcb6b8b707 \ No newline at end of file From 4cffb644c761494a19067a58e97399800e0b6886 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 20 Sep 2022 16:20:35 +0000 Subject: [PATCH 149/428] Correct a too-strict is-opfs-available check. FossilOrigin-Name: 1b5f1b4a6c1457f98c258459e23e321fc59793de298fecb84031b87f02156cd5 --- ext/wasm/api/sqlite3-api-opfs.js | 3 +-- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-opfs.js b/ext/wasm/api/sqlite3-api-opfs.js index 1f50b4631a..4e9b7328ca 100644 --- a/ext/wasm/api/sqlite3-api-opfs.js +++ b/ext/wasm/api/sqlite3-api-opfs.js @@ -77,8 +77,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ */ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri){ delete sqlite3.installOpfsVfs; - if(self.window===self || - !self.SharedArrayBuffer || + if(!self.SharedArrayBuffer || !self.FileSystemHandle || !self.FileSystemDirectoryHandle || !self.FileSystemFileHandle || diff --git a/manifest b/manifest index 54c3edc79b..907cfdd3c5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\skv-vfs-magic-names\sbranch\sinto\sfiddle-opfs\sbranch\sand\smake\ssome\skvvfs-relevant\stweaks. -D 2022-09-20T16:10:39.081 +C Correct\sa\stoo-strict\sis-opfs-available\scheck. +D 2022-09-20T16:20:35.278 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -484,7 +484,7 @@ F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 F ext/wasm/api/sqlite3-api-oo1.js f974e79d9af8f26bf33928c5730b0988cc706d14f59a5fe36394739b92249841 -F ext/wasm/api/sqlite3-api-opfs.js ce75aba0cbfb600cf839362012d17b7b2984aeac5189586c9a5a8f37a573a929 +F ext/wasm/api/sqlite3-api-opfs.js 10be4156d7db4d6aa8a456b4fb0f31a6e35c61297766e8bb55573fc5c0d56530 F ext/wasm/api/sqlite3-api-prologue.js 6f3a67c4db37e884d33a05e5cf6d9d9bc012226a18c09f33f662fefd99840a63 F ext/wasm/api/sqlite3-api-worker1.js 2eeb2a24e1a90322d84a9b88a99919b806623de62792436446099c0988f2030b F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 @@ -2025,8 +2025,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 72bebc848fce5c3b4766017d016ccb2360de2bd0cb3e47e710c80dbcb6b8b707 c5db9262d0388ccb0e84c6a4b4e2e786dd634f13874e4034ba7b175befa4ce90 -R 1f7897feb6769d430b654c4dfafdd719 +P e3d36dcdd37e59f17a07d3611d08744eb86f439fab82a648490dd608bcaa3185 +R 4e567bfc74ed899d5a6221b9c0e9380f U stephan -Z 3da2be5bc057dab3ee54a8c0dcea5576 +Z 34a126210ea63cd6428a245b533d4ca4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 054082f67b..81185f7008 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e3d36dcdd37e59f17a07d3611d08744eb86f439fab82a648490dd608bcaa3185 \ No newline at end of file +1b5f1b4a6c1457f98c258459e23e321fc59793de298fecb84031b87f02156cd5 \ No newline at end of file From 4bc2f6b415cb276f1098c427f16d71c43329dc8f Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 21 Sep 2022 08:39:03 +0000 Subject: [PATCH 150/428] Move fiddle build rules into the wasm-centric build files. Add rule to push wasm bits to the wasm test server. FossilOrigin-Name: 113f8204dc4ac849d5632d3de1680b6e0da871e107ef484c8d7273799bee3d88 --- Makefile.in | 164 +-------------- ...NS.fiddle => EXPORTED_FUNCTIONS.fiddle.in} | 0 ext/wasm/GNUmakefile | 191 +++++++++--------- ext/wasm/api/post-js-header.js | 13 +- ext/wasm/fiddle.make | 160 +++++++++++++++ ext/wasm/wasmfs.make | 1 - manifest | 21 +- manifest.uuid | 2 +- 8 files changed, 277 insertions(+), 275 deletions(-) rename ext/wasm/{EXPORTED_FUNCTIONS.fiddle => EXPORTED_FUNCTIONS.fiddle.in} (100%) create mode 100644 ext/wasm/fiddle.make diff --git a/Makefile.in b/Makefile.in index bb22039e8f..f570c7d374 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1527,166 +1527,8 @@ sqlite3.dll: $(REAL_LIBOBJ) sqlite3.def $(TCC) -shared -o $@ sqlite3.def \ -Wl,"--strip-all" $(REAL_LIBOBJ) - # -# fiddle/wasm section +# Fiddle app # -# Maintenance reminder: we can/should move this into the wasm-specific -# GNU Make makefile, but we currently need it here for access to -# $(SHELL_OPT). The rest of the wasm-related bits are handled via GNU -# Make in ext/wasm/... -# -wasm_dir = ext/wasm -wasm_dir_abs = $(TOP)/ext/wasm -# ^^^ some emcc opts require absolute paths -fiddle_dir = $(wasm_dir)/fiddle -fiddle_dir_abs = $(TOP)/$(fiddle_dir) -fiddle_module_js = $(fiddle_dir)/fiddle-module.js -sqlite3_wasm_c = $(wasm_dir)/api/sqlite3-wasm.c -$(sqlite3_wasm_c): sqlite3.c -#emcc_opt = -O0 -#emcc_opt = -O1 -#emcc_opt = -O2 -#emcc_opt = -O3 -emcc_opt = -Oz -emcc_environment = web -# WASMFS/OPFS currently (2022-08-23) does not work with fiddle -# because (A) fiddle is primarily implemented as a Worker and (B) the -# Emscripten-based Worker loading process does not properly handle the -# case of nested Workers (necessary for it to load the WASMFS-specific -# Worker thread). -emcc_flags_wasmfs = -# To enable WASMFS/OPFS, uncomment these options: -#emcc_flags_wasmfs += -sWASMFS -pthread -#emcc_environment = web,worker -#emcc_flags_wasmfs += -DSQLITE_WASM_OPFS -#emcc_flags_wasmfs += -sPTHREAD_POOL_SIZE=2 -#emcc_flags_wasmfs += -sPTHREAD_POOL_SIZE_STRICT=2 -# (Thread pool settings may require tweaking.) -#/end of WASMFS/OPFS options. -emcc_flags = $(emcc_opt) \ - -sALLOW_TABLE_GROWTH \ - -sABORTING_MALLOC \ - -sSTRICT_JS \ - -sENVIRONMENT=$(emcc_environment) \ - -sMODULARIZE \ - -sEXPORTED_RUNTIME_METHODS=@$(wasm_dir_abs)/EXPORTED_RUNTIME_METHODS.fiddle \ - -sDYNAMIC_EXECUTION=0 \ - --minify 0 \ - -I. $(SHELL_OPT) \ - -DSQLITE_THREADSAFE=0 \ - -DSQLITE_TEMP_STORE=3 \ - -DSQLITE_OMIT_UTF16 \ - -DSQLITE_OMIT_DEPRECATED \ - -DSQLITE_OMIT_SHARED_CACHE \ - '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' \ - $(emcc_flags_wasmfs) -$(fiddle_module_js): Makefile $(sqlite3_wasm_c) shell.c \ - $(wasm_dir)/EXPORTED_RUNTIME_METHODS.fiddle \ - $(wasm_dir)/EXPORTED_FUNCTIONS.fiddle - emcc -o $@ $(emcc_flags) \ - -sEXPORT_NAME=initFiddleModule \ - -sEXPORTED_FUNCTIONS=@$(wasm_dir_abs)/EXPORTED_FUNCTIONS.fiddle \ - -DSQLITE_SHELL_FIDDLE \ - $(sqlite3_wasm_c) shell.c - gzip < $@ > $@.gz - gzip < $(fiddle_dir)/fiddle-module.wasm > $(fiddle_dir)/fiddle-module.wasm.gz -$(fiddle_dir)/fiddle.js.gz: $(fiddle_dir)/fiddle.js - gzip < $< > $@ - -fiddle_generated = $(fiddle_module_js) $(fiddle_module_js).gz \ - $(fiddle_dir)/fiddle-module.wasm \ - $(fiddle_dir)/fiddle-module.wasm.gz \ - $(fiddle_dir)/fiddle.js.gz - -clean-fiddle: - rm -f $(fiddle_generated) -clean: clean-fiddle -fiddle: $(fiddle_module_js) $(fiddle_dir)/fiddle.js.gz -wasm: fiddle -######################################################################## -# Explanation of the emcc build flags follows. Full docs for these can -# be found at: -# -# https://github.com/emscripten-core/emscripten/blob/main/src/settings.js -# -# -sENVIRONMENT=web: elides bootstrap code related to non-web JS -# environments like node.js. Removing this makes the output a tiny -# 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 initializes 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! -# -# -sSTRICT_JS ensures that the emitted JS code includes the 'use -# strict' option. Note that -sSTRICT is more broadly-scoped and -# results in build errors. -# -# -sALLOW_TABLE_GROWTH is required for (at a minimum) the UDF-binding -# feature. Without it, JS functions cannot be made to proxy C-side -# callbacks. -# -# -sABORTING_MALLOC causes the JS-bound _malloc() to abort rather than -# return 0 on OOM. If set to 0 then all code which uses _malloc() -# must, just like in C, check the result before using it, else -# they're likely to corrupt the JS/WASM heap by writing to its -# address of 0. It is, as of this writing, enabled in Emscripten by -# default but we enable it explicitly in case that default changes. -# -# -sDYNAMIC_EXECUTION=0 disables eval() and the Function constructor. -# If the build runs without these, it's preferable to use this flag -# because certain execution environments disallow those constructs. -# This flag is not strictly necessary, however. -# -# -sWASM_BIGINT is UNTESTED but "should" allow the int64-using C APIs -# to work with JS/wasm, insofar as the JS environment supports the -# BigInt type. That support requires an extremely recent browser: -# Safari didn't get that support until late 2020. -# -# --no-entry: for compiling library code with no main(). If this is -# not supplied and the code has a main(), it is called as part of the -# module init process. Note that main() is #if'd out of shell.c -# (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. -# -# --minify 0: disables minification of the generated JS code, -# regardless of optimization level. Minification of the JS has -# minimal overall effect in the larger scheme of things and results -# in JS files which can neither be edited nor viewed as text files in -# Fossil (which flags them as binary because of their extreme line -# lengths). Interestingly, whether or not the comments in the -# generated JS file get stripped is unaffected by this setting and -# depends entirely on the optimization level. Higher optimization -# levels reduce the size of the JS considerably even without -# minification. -# -######################################################################## +fiddle: sqlite3.c shell.c + make -C ext/wasm fiddle diff --git a/ext/wasm/EXPORTED_FUNCTIONS.fiddle b/ext/wasm/EXPORTED_FUNCTIONS.fiddle.in similarity index 100% rename from ext/wasm/EXPORTED_FUNCTIONS.fiddle rename to ext/wasm/EXPORTED_FUNCTIONS.fiddle.in diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index e97138c2ca..79da723247 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -1,95 +1,11 @@ -# This GNU makefile exists primarily to simplify/speed up development -# of the sqlite3 WASM components. It is not part of the canonical -# build process. -# -# Maintenance notes: the fiddle build is currently performed in the -# top-level ../../Makefile.in. It may be moved into this file at some -# point, as GNU Make has been deemed acceptable for the WASM-related -# components (whereas POSIX Make is required for the more conventional -# components). -SHELL := $(shell which bash 2>/dev/null) -all: -emcc_opt ?= -O0 - -.PHONY: fiddle -ifneq (,$(wildcard /home/stephan)) - fiddle_opt ?= $(emcc_opt) -else - fiddle_opt = -Os -endif -fiddle: - $(MAKE) -C ../.. fiddle -e emcc_opt=$(fiddle_opt) -all: fiddle - -clean: - $(MAKE) -C ../../ clean-fiddle - -rm -f $(CLEAN_FILES) - -MAKEFILE := $(lastword $(MAKEFILE_LIST)) -dir.top := ../.. -# Reminder: some Emscripten flags require absolute paths -dir.wasm := $(patsubst %/,%,$(dir $(abspath $(MAKEFILE)))) -dir.api := api -dir.jacc := jaccwabyt -dir.common := common -CLEAN_FILES := *~ $(dir.jacc)/*~ $(dir.api)/*~ $(dir.common)/*~ - -sqlite3.c := $(dir.top)/sqlite3.c -$(sqlite3.c): - $(MAKE) -C $(dir.top) sqlite3.c - -SQLITE_OPT = \ - -DSQLITE_ENABLE_FTS4 \ - -DSQLITE_ENABLE_RTREE \ - -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ - -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION \ - -DSQLITE_ENABLE_STMTVTAB \ - -DSQLITE_ENABLE_DBPAGE_VTAB \ - -DSQLITE_ENABLE_DBSTAT_VTAB \ - -DSQLITE_ENABLE_BYTECODE_VTAB \ - -DSQLITE_ENABLE_OFFSET_SQL_FUNC \ - -DSQLITE_OMIT_LOAD_EXTENSION \ - -DSQLITE_OMIT_DEPRECATED \ - -DSQLITE_OMIT_UTF16 \ - -DSQLITE_OMIT_SHARED_CACHE \ - -DSQLITE_THREADSAFE=0 \ - -DSQLITE_TEMP_STORE=3 \ - -DSQLITE_OS_KV_OPTIONAL=1 -#SQLITE_OPT += -DSQLITE_ENABLE_MEMSYS5 -# ^^^ MEMSYS5 is hypothetically useful for non-Emscripten builds but -# requires adding more infrastructure and fixing one spot in the -# sqlite3 internals which calls malloc() early on. - -# SQLITE_OMIT_LOAD_EXTENSION: if this is true, sqlite3_vfs::xDlOpen -# and friends may be NULL. - -.PHONY: release -release: - $(MAKE) "emcc_opt=-Os -g3 -flto" fiddle_opt=-Os -# ^^^^^ target-specific vars, e.g.: -# release: emcc_opt=... -# apparently only work for file targets, not PHONY targets? -# -# ^^^ -flto improves runtime speed at -O0 considerably but doubles -# build time. -# -# ^^^^ -O3, -Oz, -Os minify symbol names and there appears to be no -# way around that except to use -g3, but -g3 causes the binary file -# size to absolutely explode (approx. 5x larger). This minification -# utterly breaks the resulting module, making it unsable except as -# self-contained/self-referential-only code, as ALL of the exported -# symbols get minified names. -# -# However, we have an option for using -Oz or -Os: -# -# Build with (-Os -g3) or (-Oz -g3) then use wasm-strip, from the wabt -# tools package (https://github.com/WebAssembly/wabt), to strip the -# debugging symbols. That results in a small build with unmangled -# symbol names. -Oz gives ever-so-slightly better compression than -# -Os: not quite 1% in some completely unscientific tests. Runtime -# speed for the unit tests is all over the place either way so it's -# difficult to say whether -Os gives any speed benefit over -Oz. ######################################################################## +# This GNU makefile drives the build of the sqlite3 WASM +# components. It is not part of the canonical build process. +######################################################################## +SHELL := $(shell which bash 2>/dev/null) +MAKEFILE := $(lastword $(MAKEFILE_LIST)) +all: +release: all # Emscripten SDK home dir and related binaries... EMSDK_HOME ?= $(word 1,$(wildcard $(HOME)/src/emsdk $(HOME)/emsdk)) @@ -117,13 +33,82 @@ ifeq (,$(wasm-strip)) endif endif # 'make clean' check - ifeq (,$(wasm-strip)) maybe-wasm-strip = echo "not wasm-stripping" else maybe-wasm-strip = $(wasm-strip) endif +dir.top := ../.. +# Reminder: some Emscripten flags require absolute paths +dir.wasm := $(patsubst %/,%,$(dir $(abspath $(MAKEFILE)))) +dir.api := api +dir.jacc := jaccwabyt +dir.common := common +dir.fiddle := fiddle +CLEAN_FILES := *~ $(dir.jacc)/*~ $(dir.api)/*~ $(dir.common)/*~ +sqlite3.c := $(dir.top)/sqlite3.c +SQLITE_OPT = \ + -DSQLITE_ENABLE_FTS4 \ + -DSQLITE_ENABLE_RTREE \ + -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ + -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION \ + -DSQLITE_ENABLE_STMTVTAB \ + -DSQLITE_ENABLE_DBPAGE_VTAB \ + -DSQLITE_ENABLE_DBSTAT_VTAB \ + -DSQLITE_ENABLE_BYTECODE_VTAB \ + -DSQLITE_ENABLE_OFFSET_SQL_FUNC \ + -DSQLITE_OMIT_LOAD_EXTENSION \ + -DSQLITE_OMIT_DEPRECATED \ + -DSQLITE_OMIT_UTF16 \ + -DSQLITE_OMIT_SHARED_CACHE \ + -DSQLITE_THREADSAFE=0 \ + -DSQLITE_TEMP_STORE=3 \ + -DSQLITE_OS_KV_OPTIONAL=1 \ + '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' +#SQLITE_OPT += -DSQLITE_ENABLE_MEMSYS5 +# ^^^ MEMSYS5 is hypothetically useful for non-Emscripten builds but +# requires adding more infrastructure and fixing one spot in the +# sqlite3 internals which calls malloc() early on. + +# SQLITE_OMIT_LOAD_EXTENSION: if this is true, sqlite3_vfs::xDlOpen +# and friends may be NULL. + +ifneq (,$(filter release,$(MAKECMDGOALS))) +emcc_opt ?= -Oz -g3 -flto +else +emcc_opt ?= -O0 -g +# ^^^^ build times for -O levels higher than 0 are painful at +# dev-time. +endif +# ^^^ -flto improves runtime speed at -O0 considerably but doubles +# build time. +# +# ^^^^ -O3, -Oz, -Os minify symbol names and there appears to be no +# way around that except to use -g3, but -g3 causes the binary file +# size to absolutely explode (approx. 5x larger). This minification +# utterly breaks the resulting module, making it unsable except as +# self-contained/self-referential-only code, as ALL of the exported +# symbols get minified names. +# +# However, we have an option for using -Oz or -Os: +# +# Build with (-Os -g3) or (-Oz -g3) then use wasm-strip, from the wabt +# tools package (https://github.com/WebAssembly/wabt), to strip the +# debugging symbols. That results in a small build with unmangled +# symbol names. -Oz gives ever-so-slightly better compression than +# -Os: not quite 1% in some completely unscientific tests. Runtime +# speed for the unit tests is all over the place either way so it's +# difficult to say whether -Os gives any speed benefit over -Oz. +######################################################################## + + +$(sqlite3.c): + $(MAKE) -C $(dir.top) sqlite3.c + +clean: + -rm -f $(CLEAN_FILES) + ifeq (release,$(filter release,$(MAKECMDGOALS))) ifeq (,$(wasm-strip)) $(error Cannot make release-quality binary because wasm-strip is not available. \ @@ -181,14 +166,13 @@ emcc.flags = #emcc.flags += -v # _very_ loud but also informative about what it's doing # -g is needed to keep -O2 and higher from creating broken JS via # minification. -emcc.flags += -g ######################################################################## # emcc flags for .c/.o. emcc.cflags := emcc.cflags += -std=c99 -fPIC # -------------^^^^^^^^ we currently need c99 for WASM-specific sqlite3 APIs. -emcc.cflags += -I. -I$(dir.top) # $(SQLITE_OPT) +emcc.cflags += -I. -I$(dir.top) ######################################################################## # emcc flags specific to building the final .js/.wasm file... @@ -392,7 +376,6 @@ $(speedtest1.js): $(MAKEFILE) $(speedtest1.cs) $(post-js.js) \ $(speedtest1.eflags) $(speedtest1-common.eflags) $(speedtest1.cflags) \ $(SQLITE_OPT) \ $(speedtest1.exit-runtime0) \ - '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' \ -o $@ $(speedtest1.cs) -lm $(maybe-wasm-strip) $(speedtest1.wasm) ls -la $@ $(speedtest1.wasm) @@ -451,6 +434,8 @@ oz: ######################################################################## # Sub-makes... +include fiddle.make + ######################################################################## # Some platforms do not support the WASMFS build. Raspberry Pi OS is one # of them. As such platforms are discovered, add their (uname -m) name @@ -462,3 +447,19 @@ $(info This platform does not support the WASMFS build.) else include wasmfs.make endif + +######################################################################## +# Push files to public wasm-testing.sqlite.org server +wasm-testing.include = *.wasm *.js *.html \ + batch-runner.list sql common fiddle jaccwabyt +wasm-testing.exclude = sql/speedtest1.sql +wasm-testing.dir = /jail/sites/wasm-testing +wasm-testing.dest ?= wasm-testing:$(wasm-testing.dir) +# ---------------------^^^^^^^^^^^^ ssh alias +push: + rsync -z -e ssh --ignore-times --chown=stephan:www-data --group -r \ + $(patsubst %,--exclude=%,$(wasm-testing.exclude)) \ + $(wasm-testing.include) $(wasm-testing.dest) + ssh wasm-testing 'cd $(wasm-testing.dir) && bash .gzip' || \ + echo "SSH failed: it's likely that stale content will be served via old gzip files." + diff --git a/ext/wasm/api/post-js-header.js b/ext/wasm/api/post-js-header.js index 1763188a21..f377a6541c 100644 --- a/ext/wasm/api/post-js-header.js +++ b/ext/wasm/api/post-js-header.js @@ -10,17 +10,16 @@ if(!Module.postRun) Module.postRun = []; Module.postRun.push(function(Module/*the Emscripten-style module object*/){ 'use strict'; - /* This function will contain: + /* This function will contain at least the following: - post-js-header.js (this file) - sqlite3-api-prologue.js => Bootstrapping bits to attach the rest to - - sqlite3-api-whwasmutil.js => Replacements for much of Emscripten's glue - - sqlite3-api-jaccwabyt.js => Jaccwabyt (C/JS struct binding) + - common/whwasmutil.js => Replacements for much of Emscripten's glue + - jaccwaby/jaccwabyt.js => Jaccwabyt (C/JS struct binding) - sqlite3-api-glue.js => glues previous parts together - - sqlite3-api-oo.js => SQLite3 OO API #1. - - sqlite3-api-worker.js => Worker-based API + - sqlite3-api-oo.js => SQLite3 OO API #1 + - sqlite3-api-worker1.js => Worker-based API + - sqlite3-api-opfs.js => OPFS VFS - sqlite3-api-cleanup.js => final API cleanup - post-js-footer.js => closes this postRun() function - - Whew! */ diff --git a/ext/wasm/fiddle.make b/ext/wasm/fiddle.make new file mode 100644 index 0000000000..63a665be5c --- /dev/null +++ b/ext/wasm/fiddle.make @@ -0,0 +1,160 @@ +#!/do/not/make +#^^^ help emacs select edit mode +# +# Intended to include'd by ./GNUmakefile. +####################################################################### +MAKEFILE.fiddle := $(lastword $(MAKEFILE_LIST)) + +######################################################################## +# shell.c and its build flags... +make-np-0 := make -C $(dir.top) -n -p +make-np-1 := sed -e 's/(TOP)/(dir.top)/g' +$(eval $(shell $(make-np-0) | grep -e '^SHELL_OPT ' | $(make-np-1))) +$(eval $(shell $(make-np-0) | grep -e '^SHELL_SRC ' | $(make-np-1))) +# ^^^ can't do that in 1 invocation b/c newlines get stripped +ifeq (,$(SHELL_OPT)) +$(error Could not parse SHELL_OPT from $(dir.top)/Makefile.) +endif +ifeq (,$(SHELL_SRC)) +$(error Could not parse SHELL_SRC from $(dir.top)/Makefile.) +endif +$(dir.top)/shell.c: $(SHELL_SRC) $(dir.top)/tool/mkshellc.tcl + $(MAKE) -C $(dir.top) shell.c +# /shell.c +######################################################################## + +fiddle.emcc-flags = \ + $(emcc.cflags) $(emcc_opt) \ + --minify 0 \ + -sALLOW_TABLE_GROWTH \ + -sABORTING_MALLOC \ + -sSTRICT_JS \ + -sENVIRONMENT=web,worker \ + -sMODULARIZE \ + -sDYNAMIC_EXECUTION=0 \ + -sEXPORT_NAME=initFiddleModule \ + -sEXPORTED_RUNTIME_METHODS=@$(dir.wasm)/EXPORTED_RUNTIME_METHODS.fiddle \ + -sEXPORTED_FUNCTIONS=@$(dir.wasm)/EXPORTED_FUNCTIONS.fiddle \ + --post-js=$(post-js.js) \ + $(SQLITE_OPT) $(SHELL_OPT) \ + -D_POSIX_SOURCE \ + -DSQLITE_SHELL_FIDDLE +# -D_POSIX_C_SOURCE is needed for strdup() with emcc + +fiddle.EXPORTED_FUNCTIONS.in := \ + EXPORTED_FUNCTIONS.fiddle.in \ + EXPORTED_FUNCTIONS.api + +EXPORTED_FUNCTIONS.fiddle: $(fiddle.EXPORTED_FUNCTIONS.in) $(MAKEFILE.fiddle) + grep -h -v jaccwabyt $(fiddle.EXPORTED_FUNCTIONS.in) | sort -u > $@ + +fiddle-module.js := $(dir.fiddle)/fiddle-module.js +fiddle-module.wasm := $(subst .js,.wasm,$(fiddle-module.js)) +fiddle.cs := $(dir.top)/shell.c $(sqlite3-wasm.c) + +$(fiddle-module.js): $(MAKEFILE) $(MAKEFILE.fiddle) \ + EXPORTED_FUNCTIONS.fiddle EXPORTED_RUNTIME_METHODS.fiddle \ + $(fiddle.cs) $(post-js.js) + $(emcc.bin) -o $@ $(fiddle.emcc-flags) $(fiddle.cs) + $(maybe-wasm-strip) $(fiddle-module.wasm) + gzip < $@ > $@.gz + gzip < $(fiddle-module.wasm) > $(fiddle-module.wasm).gz + +$(dir.fiddle)/fiddle.js.gz: $(dir.fiddle)/fiddle.js + gzip < $< > $@ + +clean: clean-fiddle +clean-fiddle: + rm -f $(fiddle-module.js) $(fiddle-module.js).gz \ + $(fiddle-module.wasm) $(fiddle-module.wasm).gz \ + EXPORTED_FUNCTIONS.fiddle +.PHONY: fiddle +fiddle: $(fiddle-module.js) $(dir.fiddle)/fiddle.js.gz +all: fiddle + +######################################################################## +# Explanation of the emcc build flags follows. Full docs for these can +# be found at: +# +# https://github.com/emscripten-core/emscripten/blob/main/src/settings.js +# +# -sENVIRONMENT=web: elides bootstrap code related to non-web JS +# environments like node.js. Removing this makes the output a tiny +# 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 initializes 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! +# +# -sSTRICT_JS ensures that the emitted JS code includes the 'use +# strict' option. Note that -sSTRICT is more broadly-scoped and +# results in build errors. +# +# -sALLOW_TABLE_GROWTH is required for (at a minimum) the UDF-binding +# feature. Without it, JS functions cannot be made to proxy C-side +# callbacks. +# +# -sABORTING_MALLOC causes the JS-bound _malloc() to abort rather than +# return 0 on OOM. If set to 0 then all code which uses _malloc() +# must, just like in C, check the result before using it, else +# they're likely to corrupt the JS/WASM heap by writing to its +# address of 0. It is, as of this writing, enabled in Emscripten by +# default but we enable it explicitly in case that default changes. +# +# -sDYNAMIC_EXECUTION=0 disables eval() and the Function constructor. +# If the build runs without these, it's preferable to use this flag +# because certain execution environments disallow those constructs. +# This flag is not strictly necessary, however. +# +# -sWASM_BIGINT is UNTESTED but "should" allow the int64-using C APIs +# to work with JS/wasm, insofar as the JS environment supports the +# BigInt type. That support requires an extremely recent browser: +# Safari didn't get that support until late 2020. +# +# --no-entry: for compiling library code with no main(). If this is +# not supplied and the code has a main(), it is called as part of the +# module init process. Note that main() is #if'd out of shell.c +# (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. +# +# --minify 0: disables minification of the generated JS code, +# regardless of optimization level. Minification of the JS has +# minimal overall effect in the larger scheme of things and results +# in JS files which can neither be edited nor viewed as text files in +# Fossil (which flags them as binary because of their extreme line +# lengths). Interestingly, whether or not the comments in the +# generated JS file get stripped is unaffected by this setting and +# depends entirely on the optimization level. Higher optimization +# levels reduce the size of the JS considerably even without +# minification. +# +######################################################################## diff --git a/ext/wasm/wasmfs.make b/ext/wasm/wasmfs.make index 253237a237..d65684b637 100644 --- a/ext/wasm/wasmfs.make +++ b/ext/wasm/wasmfs.make @@ -26,7 +26,6 @@ sqlite3-wasmfs.cflags += -std=c99 -fPIC -g sqlite3-wasmfs.cflags += -pthread sqlite3-wasmfs.cflags += -I. -I.. -I$(dir.top) sqlite3-wasmfs.cflags += $(SQLITE_OPT) -DSQLITE_WASM_WASMFS -sqlite3-wasmfs.cflags += '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' sqlite3-wasmfs.extra.c := ifeq (1,1) diff --git a/manifest b/manifest index 907cfdd3c5..2b32ddbe21 100644 --- a/manifest +++ b/manifest @@ -1,9 +1,9 @@ -C Correct\sa\stoo-strict\sis-opfs-available\scheck. -D 2022-09-20T16:20:35.278 +C Move\sfiddle\sbuild\srules\sinto\sthe\swasm-centric\sbuild\sfiles.\sAdd\srule\sto\spush\swasm\sbits\sto\sthe\swasm\stest\sserver. +D 2022-09-21T08:39:03.936 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in 50e421194df031f669667fdb238c54959ecbea5a0b97dd3ed776cffbeea926d5 +F Makefile.in 2fbc1de74212fba94e0c3eca5549b894c91c1c17fb5e6fd23ca90a616523c5f5 F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 F Makefile.msc d547a2fdba38a1c6cd1954977d0b0cc017f5f8fbfbc65287bf8d335808938016 F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e @@ -472,15 +472,15 @@ F ext/session/test_session.c f433f68a8a8c64b0f5bc74dc725078f12483301ad4ae8375205 F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb -F ext/wasm/EXPORTED_FUNCTIONS.fiddle 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3 +F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3 w ext/wasm/EXPORTED_FUNCTIONS.fiddle F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 0635cb6e90787b2d06ae51d903444214e8030274554a2406136881fed3c1fad6 +F ext/wasm/GNUmakefile 45b2815443729ed7de33acb4a2716038f8a41005529cd96bac81f7f0d97d1ded F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 8a724a674bd2089eef9676b434c0ab709da00db33f73a94e4987e90169b1cd14 F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 F ext/wasm/api/README.md d876597edd2b9542b6ea031adaaff1c042076fde7b670b1dc6d8a87b28a6631b F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c -F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b +F ext/wasm/api/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1ecc5ba9e64ef90a8b F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 F ext/wasm/api/sqlite3-api-oo1.js f974e79d9af8f26bf33928c5730b0988cc706d14f59a5fe36394739b92249841 @@ -500,6 +500,7 @@ F ext/wasm/demo-123.html aa281d33b7eefa755f3122b7b5a18f39a42dc5fb69c8879171bf14b F ext/wasm/demo-123.js 234655683e35a4543a23de7b10800d76b0369947b33e089e5613171fa7795afb F ext/wasm/demo-kvvfs1.html 7d4f28873de67f51ac18c584b7d920825139866a96049a49c424d6f5a0ea5e7f F ext/wasm/demo-kvvfs1.js e884ea35022d772c0d1dd884b40011413696438394f605c6cd4808cfb1642a4a +F ext/wasm/fiddle.make 8b1018e8d91b6d3add93813302a4acf2e5693dd9882499e38eccaec73afb1f79 F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 @@ -529,7 +530,7 @@ F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d766 F ext/wasm/testing1.js 507001a970fe8a8eb67b6c8d783e1c1daa3db2719f727c4551af29349410e538 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c -F ext/wasm/wasmfs.make 0fbe3b4ef4e5e25ed61d7b581c48e6406dd688443d1b8d4daf94d779a8056c54 +F ext/wasm/wasmfs.make b24fa0199ddf9720ae8b0c7751a5dbdb2501c8d7c57ecf8c2e0a80ebf4b2d00b F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2025,8 +2026,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e3d36dcdd37e59f17a07d3611d08744eb86f439fab82a648490dd608bcaa3185 -R 4e567bfc74ed899d5a6221b9c0e9380f +P 1b5f1b4a6c1457f98c258459e23e321fc59793de298fecb84031b87f02156cd5 +R 00dda2c5015c98fa272f7dff83ac1143 U stephan -Z 34a126210ea63cd6428a245b533d4ca4 +Z c50873d6fe9ef43a35ee904cdd659788 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 81185f7008..de2b0356aa 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1b5f1b4a6c1457f98c258459e23e321fc59793de298fecb84031b87f02156cd5 \ No newline at end of file +113f8204dc4ac849d5632d3de1680b6e0da871e107ef484c8d7273799bee3d88 \ No newline at end of file From ebe14230988ce62ceac1afb19da13a51fbfe749d Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 21 Sep 2022 08:41:40 +0000 Subject: [PATCH 151/428] Add a link to fiddle.html in the wasm test app index.html. FossilOrigin-Name: 96c734c07acfbea153d9aaf293a28a2d49d78de19fec4fb90c4c08fb1fd0ddbd --- ext/wasm/index.html | 2 ++ manifest | 14 +++++++------- manifest.uuid | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/ext/wasm/index.html b/ext/wasm/index.html index e9a8d7bebb..83ee85b71c 100644 --- a/ext/wasm/index.html +++ b/ext/wasm/index.html @@ -36,6 +36,8 @@
    The tests...
      +
    • fiddle is an HTML front-end + to a wasm build of the sqlite3 shell.
    • demo-123 provides a no-nonsense example of adding sqlite3 support to a web page.
    • diff --git a/manifest b/manifest index 2b32ddbe21..6424e629f0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Move\sfiddle\sbuild\srules\sinto\sthe\swasm-centric\sbuild\sfiles.\sAdd\srule\sto\spush\swasm\sbits\sto\sthe\swasm\stest\sserver. -D 2022-09-21T08:39:03.936 +C Add\sa\slink\sto\sfiddle.html\sin\sthe\swasm\stest\sapp\sindex.html. +D 2022-09-21T08:41:40.128 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -472,7 +472,7 @@ F ext/session/test_session.c f433f68a8a8c64b0f5bc74dc725078f12483301ad4ae8375205 F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb -F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3 w ext/wasm/EXPORTED_FUNCTIONS.fiddle +F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 F ext/wasm/GNUmakefile 45b2815443729ed7de33acb4a2716038f8a41005529cd96bac81f7f0d97d1ded F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 @@ -505,7 +505,7 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 -F ext/wasm/index.html 4b74ea334f0bba9923a61246d807a1873784fcef4d3fb342b698613d0a29fd9c +F ext/wasm/index.html 5cb26db659696ef59e55a2d61a3c1cb924843527b3265e3449d9b718cf041778 F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f @@ -2026,8 +2026,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 1b5f1b4a6c1457f98c258459e23e321fc59793de298fecb84031b87f02156cd5 -R 00dda2c5015c98fa272f7dff83ac1143 +P 113f8204dc4ac849d5632d3de1680b6e0da871e107ef484c8d7273799bee3d88 +R 337ac284fad2396684acb37bb3bd23e5 U stephan -Z c50873d6fe9ef43a35ee904cdd659788 +Z 31d6498de398e0445783a30c20bccccb # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index de2b0356aa..2c8cf8c72f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -113f8204dc4ac849d5632d3de1680b6e0da871e107ef484c8d7273799bee3d88 \ No newline at end of file +96c734c07acfbea153d9aaf293a28a2d49d78de19fec4fb90c4c08fb1fd0ddbd \ No newline at end of file From 171b168b3e844e324f72cd7a7db9573964191435 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 21 Sep 2022 12:25:40 +0000 Subject: [PATCH 152/428] Added some structure to the wasm demo link list. FossilOrigin-Name: 777077c4c2249e1ec78390d4f65aaf281c1fbefcef4bcc7609199e995645ceb6 --- ext/wasm/index.html | 78 ++++++++++++++++++++++++++------------------- manifest | 12 +++---- manifest.uuid | 2 +- 3 files changed, 53 insertions(+), 39 deletions(-) diff --git a/ext/wasm/index.html b/ext/wasm/index.html index 83ee85b71c..7b541a6457 100644 --- a/ext/wasm/index.html +++ b/ext/wasm/index.html @@ -36,38 +36,52 @@
    The tests...
      -
    • fiddle is an HTML front-end - to a wasm build of the sqlite3 shell.
    • -
    • demo-123 provides a - no-nonsense example of adding sqlite3 support to a - web page.
    • -
    • demo-123-worker is the - same as demo-123 but loads and run sqlite3 from - a Worker thread.
    • -
    • testing1: sanity tests of the core APIs and surrounding utility code.
    • -
    • testing2: Worker-based test of OO API #1.
    • -
    • testing-worker1-promiser: - tests for the Promise-based wrapper of the Worker-based API.
    • -
    • batch-runner: runs batches of SQL exported from speedtest1.
    • -
    • speedtest1: a main-thread WASM build of speedtest1.
    • -
    • speedtest1-worker: an interactive Worker-thread variant of speedtest1.
    • -
    • speedtest1-wasmfs: a variant of speedtest1 built solely for the wasmfs/opfs feature.
    • -
    • speedtest1-kvvfs: speedtest1 with the kvvfs.
    • -
    • demo-kvvfs1: very basic - demo of using the key-value vfs for storing a persistent db - in JS localStorage or sessionStorage.
    • -
    • scratchpad-wasmfs-main: - experimenting with WASMFS/OPFS-based persistence. Maintenance - reminder: we cannot currently (2022-09-15) load WASMFS in a - worker due to an Emscripten limitation.
    • -
    • test-opfs-vfs - (same - with verbose output and sanity-checking tests) is an - experiment in implementing a syncronous sqlite3 VFS proxy - for a fully asynchronous backend interface (namely OPFS), - using SharedArrayBuffer and the Atomics APIs to regulate - communication between the synchronous interface and the - async impl. +
    • High-level apps and demos... +
        +
      • fiddle is an HTML front-end + to a wasm build of the sqlite3 shell.
      • +
      • demo-123 provides a + no-nonsense example of adding sqlite3 support to a + web page.
      • +
      • demo-123-worker is the + same as demo-123 but loads and run sqlite3 from + a Worker thread.
      • +
      • demo-kvvfs1: very basic + demo of using the key-value vfs for storing a persistent db + in JS localStorage or sessionStorage.
      • +
      +
    • +
    • speedtest1 ports (sqlite3's primary benchmarking tool)... + +
    • +
    • The obligatory "misc." category... +
        +
      • testing1: sanity tests of the core APIs and surrounding utility code.
      • +
      • testing2: Worker-based test of OO API #1.
      • +
      • testing-worker1-promiser: + tests for the Promise-based wrapper of the Worker-based API.
      • +
      • batch-runner: runs batches of SQL exported from speedtest1.
      • +
      • scratchpad-wasmfs-main: + experimenting with WASMFS/OPFS-based persistence. Maintenance + reminder: we cannot currently (2022-09-15) load WASMFS in a + worker due to an Emscripten limitation.
      • +
      • test-opfs-vfs + (same + with verbose output and sanity-checking tests) is an + experiment in implementing a syncronous sqlite3 VFS proxy + for a fully asynchronous backend interface (namely OPFS), + using SharedArrayBuffer and the Atomics APIs to regulate + communication between the synchronous interface and the + async impl. +
      • +
    diff --git a/manifest b/manifest index 6424e629f0..6bf77f2f6c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sa\slink\sto\sfiddle.html\sin\sthe\swasm\stest\sapp\sindex.html. -D 2022-09-21T08:41:40.128 +C Added\ssome\sstructure\sto\sthe\swasm\sdemo\slink\slist. +D 2022-09-21T12:25:40.497 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -505,7 +505,7 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 -F ext/wasm/index.html 5cb26db659696ef59e55a2d61a3c1cb924843527b3265e3449d9b718cf041778 +F ext/wasm/index.html 8b4b7ea052d558262c8466f94326fb455c21049b2d1d3577ed0a5fce15101ba8 F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f @@ -2026,8 +2026,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 113f8204dc4ac849d5632d3de1680b6e0da871e107ef484c8d7273799bee3d88 -R 337ac284fad2396684acb37bb3bd23e5 +P 96c734c07acfbea153d9aaf293a28a2d49d78de19fec4fb90c4c08fb1fd0ddbd +R 861bbf4cfa9812be3b9491caf1943540 U stephan -Z 31d6498de398e0445783a30c20bccccb +Z 172073e3d6aad2306bf7614651c7edbb # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2c8cf8c72f..17b72d2509 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -96c734c07acfbea153d9aaf293a28a2d49d78de19fec4fb90c4c08fb1fd0ddbd \ No newline at end of file +777077c4c2249e1ec78390d4f65aaf281c1fbefcef4bcc7609199e995645ceb6 \ No newline at end of file From 72ab400d4db6a429bf2a2758c1c39afc5178f530 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 21 Sep 2022 12:27:35 +0000 Subject: [PATCH 153/428] Doc cleanups and additions. Add a way for the OPFS async runner to propagate exception text to the calling thread. FossilOrigin-Name: 5c5e80652825cf883e6c17809cb98f2bf17d5feac2d263f6f492479154730dab --- ext/wasm/api/sqlite3-api-opfs.js | 115 +++++++++++++++++++-------- ext/wasm/api/sqlite3-wasm.c | 8 +- ext/wasm/sqlite3-opfs-async-proxy.js | 108 ++++++++++++------------- manifest | 16 ++-- manifest.uuid | 2 +- 5 files changed, 149 insertions(+), 100 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-opfs.js b/ext/wasm/api/sqlite3-api-opfs.js index 4e9b7328ca..a117d2725e 100644 --- a/ext/wasm/api/sqlite3-api-opfs.js +++ b/ext/wasm/api/sqlite3-api-opfs.js @@ -16,9 +16,7 @@ Worker, implemented in sqlite3-opfs-async-proxy.js. This file is intended to be appended to the main sqlite3 JS deliverable somewhere after sqlite3-api-glue.js and before sqlite3-api-cleanup.js. - */ - 'use strict'; self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ /** @@ -314,55 +312,98 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) const t = performance.now(); Atomics.wait(state.sabOPView, state.opIds.rc, -1); const rc = Atomics.load(state.sabOPView, state.opIds.rc); + if(rc){ + const err = state.s11n.deserialize(); + if(err) error(op+"() async error:",...err); + } metrics[op].wait += performance.now() - t; return rc; }; const initS11n = ()=>{ /** - ACHTUNG: this code is 100% duplicated in the other half of - this proxy! + ACHTUNG: this code is 100% duplicated in the other half of this + proxy! The documentation is maintained in the "synchronous half". - Historical note: this impl was initially about 5% this size by using - using JSON.stringify/parse(), but using fit-to-purpose serialization - saves considerable runtime. + This proxy de/serializes cross-thread function arguments and + output-pointer values via the state.sabIO SharedArrayBuffer, + using the region defined by (state.sabS11nOffset, + state.sabS11nOffset]. Only one dataset is recorded at a time. + + This is not a general-purpose format. It only supports the range + of operations, and data sizes, needed by the sqlite3_vfs and + sqlite3_io_methods operations. + + The data format can be succinctly summarized as: + + Nt...Td...D + + Where: + + - N = number of entries (1 byte) + + - t = type ID of first argument (1 byte) + + - ...T = type IDs of the 2nd and subsequent arguments (1 byte + each). + + - d = raw bytes of first argument (per-type size). + + - ...D = raw bytes of the 2nd and subsequent arguments (per-type + size). + + All types except strings have fixed sizes. Strings are stored + using their TextEncoder/TextDecoder representations. It would + arguably make more sense to store them as Int16Arrays of + their JS character values, but how best/fastest to get that + in and out of string form us an open point. + + Historical note: this impl was initially about 1% this size by + using using JSON.stringify/parse(), but using fit-to-purpose + serialization saves considerable runtime. */ if(state.s11n) return state.s11n; const textDecoder = new TextDecoder(), - textEncoder = new TextEncoder('utf-8'), - viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), - viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize); + textEncoder = new TextEncoder('utf-8'), + viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), + viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize); state.s11n = Object.create(null); + /* Only arguments and return values of these types may be + serialized. This covers the whole range of types needed by the + sqlite3_vfs API. */ const TypeIds = Object.create(null); TypeIds.number = { id: 1, size: 8, getter: 'getFloat64', setter: 'setFloat64' }; TypeIds.bigint = { id: 2, size: 8, getter: 'getBigInt64', setter: 'setBigInt64' }; TypeIds.boolean = { id: 3, size: 4, getter: 'getInt32', setter: 'setInt32' }; TypeIds.string = { id: 4 }; - const getTypeId = (v)=>{ - return TypeIds[typeof v] || toss("This value type cannot be serialized.",v); - }; + + const getTypeId = (v)=>( + TypeIds[typeof v] + || toss("Maintenance required: this value type cannot be serialized.",v) + ); const getTypeIdById = (tid)=>{ switch(tid){ - case TypeIds.number.id: return TypeIds.number; - case TypeIds.bigint.id: return TypeIds.bigint; - case TypeIds.boolean.id: return TypeIds.boolean; - case TypeIds.string.id: return TypeIds.string; - default: toss("Invalid type ID:",tid); + case TypeIds.number.id: return TypeIds.number; + case TypeIds.bigint.id: return TypeIds.bigint; + case TypeIds.boolean.id: return TypeIds.boolean; + case TypeIds.string.id: return TypeIds.string; + default: toss("Invalid type ID:",tid); } }; + /** - Returns an array of the state serialized by the most recent - serialize() operation (here or in the counterpart thread), or - null if the serialization buffer is empty. + Returns an array of the deserialized state stored by the most + recent serialize() operation (from from this thread or the + counterpart thread), or null if the serialization buffer is empty. */ state.s11n.deserialize = function(){ ++metrics.s11n.deserialize.count; const t = performance.now(); - let rc = null; const argc = viewU8[0]; + const rc = argc ? [] : null; if(argc){ - rc = []; - let offset = 1, i, n, v, typeIds = []; + const typeIds = []; + let offset = 1, i, n, v; for(i = 0; i < argc; ++i, ++offset){ typeIds.push(getTypeIdById(viewU8[offset])); } @@ -371,7 +412,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) if(t.getter){ v = viewDV[t.getter](offset, state.littleEndian); offset += t.size; - }else{ + }else{/*String*/ n = viewDV.getInt32(offset, state.littleEndian); offset += 4; v = textDecoder.decode(viewU8.slice(offset, offset+n)); @@ -384,6 +425,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) metrics.s11n.deserialize.time += performance.now() - t; return rc; }; + /** Serializes all arguments to the shared buffer for consumption by the counterpart thread. @@ -397,22 +439,27 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) state. */ state.s11n.serialize = function(...args){ - ++metrics.s11n.serialize.count; const t = performance.now(); + ++metrics.s11n.serialize.count; if(args.length){ //log("serialize():",args); - let i = 0, offset = 1, typeIds = []; - viewU8[0] = args.length & 0xff; + const typeIds = []; + let i = 0, offset = 1; + viewU8[0] = args.length & 0xff /* header = # of args */; for(; i < args.length; ++i, ++offset){ + /* Write the TypeIds.id value into the next args.length + bytes. */ typeIds.push(getTypeId(args[i])); viewU8[offset] = typeIds[i].id; } for(i = 0; i < args.length; ++i) { + /* Deserialize the following bytes based on their + corresponding TypeIds.id from the header. */ const t = typeIds[i]; if(t.setter){ viewDV[t.setter](offset, args[i], state.littleEndian); offset += t.size; - }else{ + }else{/*String*/ const s = textEncoder.encode(args[i]); viewDV.setInt32(offset, s.byteLength, state.littleEndian); offset += 4; @@ -774,7 +821,10 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) but cannot report the nature of the failure. */ opfsUtil.deleteEntry = function(fsEntryName,recursive=false){ - return 0===opRun('xDelete', fsEntryName, 0, recursive); + mTimeStart('xDelete'); + const rc = opRun('xDelete', fsEntryName, 0, recursive); + mTimeEnd(); + return 0===rc; }; /** Synchronously creates the given directory name, recursively, in @@ -782,7 +832,10 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) directory already exists, else false. */ opfsUtil.mkdir = function(absDirName){ - return 0===opRun('mkdir', absDirName); + mTimeStart('mkdir'); + const rc = opRun('mkdir', absDirName); + mTimeEnd(); + return 0===rc; }; /** Synchronously checks whether the given OPFS filesystem exists, diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index eb8f58b402..5b127cb743 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -9,10 +9,10 @@ /* ** WASM_KEEP is identical to EMSCRIPTEN_KEEPALIVE but is not -** Emscripten-specific. It explicitly includes marked functions for -** export into the target wasm file without requiring explicit listing -** of those functions in Emscripten's -sEXPORTED_FUNCTIONS=... list -** (or equivalent in other build platforms). Any function with neither +** Emscripten-specific. It explicitly marks functions for export into +** the target wasm file without requiring explicit listing of those +** functions in Emscripten's -sEXPORTED_FUNCTIONS=... list (or +** equivalent in other build platforms). Any function with neither ** this attribute nor which is listed as an explicit export will not ** be exported from the wasm file (but may still be used internally ** within the wasm file). diff --git a/ext/wasm/sqlite3-opfs-async-proxy.js b/ext/wasm/sqlite3-opfs-async-proxy.js index 7c5e8430b6..06bc235ab2 100644 --- a/ext/wasm/sqlite3-opfs-async-proxy.js +++ b/ext/wasm/sqlite3-opfs-async-proxy.js @@ -136,8 +136,8 @@ const getDirForPath = async function f(absFilename, createDirs = false){ /** - Stores the given value at the array index reserved for the given op - and then Atomics.notify()'s it. + Stores the given value at state.sabOPView[state.opIds.rc] and then + Atomics.notify()'s it. */ const storeAndNotify = (opName, value)=>{ log(opName+"() => notify(",state.opIds.rc,",",value,")"); @@ -190,10 +190,11 @@ const vfsAsyncImpls = { try { await getDirForPath(dirname+"/filepart", true); }catch(e){ - //error("mkdir failed",filename, e.message); + state.s11n.serialize(e.message); rc = state.sq3Codes.SQLITE_IOERR; + }finally{ + wTimeEnd(); } - wTimeEnd(); storeAndNotify('mkdir', rc); mTimeEnd(); }, @@ -216,9 +217,11 @@ const vfsAsyncImpls = { const [dh, fn] = await getDirForPath(filename); await dh.getFileHandle(fn); }catch(e){ + state.s11n.serialize(e.message); rc = state.sq3Codes.SQLITE_IOERR; + }finally{ + wTimeEnd(); } - wTimeEnd(); storeAndNotify('xAccess', rc); mTimeEnd(); }, @@ -236,6 +239,7 @@ const vfsAsyncImpls = { catch(e){ warn("Ignoring dirHandle.removeEntry() failure of",fh,e) } } }else{ + state.s11n.serialize(); rc = state.sq3Codes.SQLITE_NOTFOUND; } wTimeEnd(); @@ -274,9 +278,7 @@ const vfsAsyncImpls = { filename = filename.join('/'); } }catch(e){ - /* Ignoring: _presumably_ the file can't be found or a dir is - not empty. */ - //error("Delete failed",filename, e.message); + state.s11n.serialize(e.message); rc = state.sq3Codes.SQLITE_IOERR_DELETE; } wTimeEnd(); @@ -292,7 +294,7 @@ const vfsAsyncImpls = { state.s11n.serialize(Number(sz)); sz = 0; }catch(e){ - error("xFileSize():",e, fh); + state.s11n.serialize(e.message); sz = state.sq3Codes.SQLITE_IOERR; } wTimeEnd(); @@ -337,6 +339,7 @@ const vfsAsyncImpls = { }catch(e){ wTimeEnd(); error(opName,e); + state.s11n.serialize(e.message); storeAndNotify(opName, state.sq3Codes.SQLITE_IOERR); } mTimeEnd(); @@ -358,6 +361,7 @@ const vfsAsyncImpls = { } }catch(e){ error("xRead() failed",e,fh); + state.s11n.serialize(e.message); rc = state.sq3Codes.SQLITE_IOERR_READ; } storeAndNotify('xRead',rc); @@ -366,12 +370,18 @@ const vfsAsyncImpls = { xSync: async function(fid,flags/*ignored*/){ mTimeStart('xSync'); const fh = __openFiles[fid]; + let rc = 0; if(!fh.readOnly && fh.accessHandle){ - wTimeStart('xSync'); - await fh.accessHandle.flush(); - wTimeEnd(); + try { + wTimeStart('xSync'); + await fh.accessHandle.flush(); + }catch(e){ + state.s11n.serialize(e.message); + }finally{ + wTimeEnd(); + } } - storeAndNotify('xSync',0); + storeAndNotify('xSync',rc); mTimeEnd(); }, xTruncate: async function(fid,size){ @@ -384,6 +394,7 @@ const vfsAsyncImpls = { await fh.accessHandle.truncate(size); }catch(e){ error("xTruncate():",e,fh); + state.s11n.serialize(e.message); rc = state.sq3Codes.SQLITE_IOERR_TRUNCATE; } wTimeEnd(); @@ -403,6 +414,7 @@ const vfsAsyncImpls = { ) ? 0 : state.sq3Codes.SQLITE_IOERR_WRITE; }catch(e){ error("xWrite():",e,fh); + state.s11n.serialize(e.message); rc = state.sq3Codes.SQLITE_IOERR_WRITE; }finally{ wTimeEnd(); @@ -413,53 +425,42 @@ const vfsAsyncImpls = { }; const initS11n = ()=>{ - // Achtung: this code is 100% duplicated in the other half of this proxy! - /** - Historical note: this impl was initially about 1% this size by using - using JSON.stringify/parse(), but using fit-to-purpose serialization - saves considerable runtime. + ACHTUNG: this code is 100% duplicated in the other half of this + proxy! The documentation is maintained in the "synchronous half". */ - if(state.s11n) return state.s11n; const textDecoder = new TextDecoder(), textEncoder = new TextEncoder('utf-8'), viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize), viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize); state.s11n = Object.create(null); - const TypeIds = Object.create(null); TypeIds.number = { id: 1, size: 8, getter: 'getFloat64', setter: 'setFloat64' }; TypeIds.bigint = { id: 2, size: 8, getter: 'getBigInt64', setter: 'setBigInt64' }; TypeIds.boolean = { id: 3, size: 4, getter: 'getInt32', setter: 'setInt32' }; TypeIds.string = { id: 4 }; - - const getTypeId = (v)=>{ - return TypeIds[typeof v] || toss("This value type cannot be serialized.",v); - }; + const getTypeId = (v)=>( + TypeIds[typeof v] + || toss("Maintenance required: this value type cannot be serialized.",v) + ); const getTypeIdById = (tid)=>{ switch(tid){ - case TypeIds.number.id: return TypeIds.number; - case TypeIds.bigint.id: return TypeIds.bigint; - case TypeIds.boolean.id: return TypeIds.boolean; - case TypeIds.string.id: return TypeIds.string; - default: toss("Invalid type ID:",tid); + case TypeIds.number.id: return TypeIds.number; + case TypeIds.bigint.id: return TypeIds.bigint; + case TypeIds.boolean.id: return TypeIds.boolean; + case TypeIds.string.id: return TypeIds.string; + default: toss("Invalid type ID:",tid); } }; - - /** - Returns an array of the state serialized by the most recent - serialize() operation (here or in the counterpart thread), or - null if the serialization buffer is empty. - */ state.s11n.deserialize = function(){ ++metrics.s11n.deserialize.count; const t = performance.now(); - let rc = null; const argc = viewU8[0]; + const rc = argc ? [] : null; if(argc){ - rc = []; - let offset = 1, i, n, v, typeIds = []; + const typeIds = []; + let offset = 1, i, n, v; for(i = 0; i < argc; ++i, ++offset){ typeIds.push(getTypeIdById(viewU8[offset])); } @@ -468,7 +469,7 @@ const initS11n = ()=>{ if(t.getter){ v = viewDV[t.getter](offset, state.littleEndian); offset += t.size; - }else{ + }else{/*String*/ n = viewDV.getInt32(offset, state.littleEndian); offset += 4; v = textDecoder.decode(viewU8.slice(offset, offset+n)); @@ -481,36 +482,28 @@ const initS11n = ()=>{ metrics.s11n.deserialize.time += performance.now() - t; return rc; }; - - /** - Serializes all arguments to the shared buffer for consumption - by the counterpart thread. - - This routine is only intended for serializing OPFS VFS - arguments and (in at least one special case) result values, - and the buffer is sized to be able to comfortably handle - those. - - If passed no arguments then it zeroes out the serialization - state. - */ state.s11n.serialize = function(...args){ - ++metrics.s11n.serialize.count; const t = performance.now(); + ++metrics.s11n.serialize.count; if(args.length){ //log("serialize():",args); - let i = 0, offset = 1, typeIds = []; - viewU8[0] = args.length & 0xff; + const typeIds = []; + let i = 0, offset = 1; + viewU8[0] = args.length & 0xff /* header = # of args */; for(; i < args.length; ++i, ++offset){ + /* Write the TypeIds.id value into the next args.length + bytes. */ typeIds.push(getTypeId(args[i])); viewU8[offset] = typeIds[i].id; } for(i = 0; i < args.length; ++i) { + /* Deserialize the following bytes based on their + corresponding TypeIds.id from the header. */ const t = typeIds[i]; if(t.setter){ viewDV[t.setter](offset, args[i], state.littleEndian); offset += t.size; - }else{ + }else{/*String*/ const s = textEncoder.encode(args[i]); viewDV.setInt32(offset, s.byteLength, state.littleEndian); offset += 4; @@ -548,6 +541,9 @@ const waitLoop = async function f(){ Atomics.store(state.sabOPView, state.opIds.whichOp, 0); const hnd = opHandlers[opId] ?? toss("No waitLoop handler for whichOp #",opId); const args = state.s11n.deserialize(); + state.s11n.serialize()/* clear s11n to keep the caller from + confusing this with an exception string + written by the upcoming operation */; //warn("waitLoop() whichOp =",opId, hnd, args); if(hnd.f) await hnd.f(...args); else error("Missing callback for opId",opId); diff --git a/manifest b/manifest index 6bf77f2f6c..d30df61098 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Added\ssome\sstructure\sto\sthe\swasm\sdemo\slink\slist. -D 2022-09-21T12:25:40.497 +C Doc\scleanups\sand\sadditions.\sAdd\sa\sway\sfor\sthe\sOPFS\sasync\srunner\sto\spropagate\sexception\stext\sto\sthe\scalling\sthread. +D 2022-09-21T12:27:35.940 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -484,11 +484,11 @@ F ext/wasm/api/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1 F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 F ext/wasm/api/sqlite3-api-oo1.js f974e79d9af8f26bf33928c5730b0988cc706d14f59a5fe36394739b92249841 -F ext/wasm/api/sqlite3-api-opfs.js 10be4156d7db4d6aa8a456b4fb0f31a6e35c61297766e8bb55573fc5c0d56530 +F ext/wasm/api/sqlite3-api-opfs.js dbbce38b0cd89d1eaf829546e2999241127150a40ff2e0331d842a1f31c756e5 F ext/wasm/api/sqlite3-api-prologue.js 6f3a67c4db37e884d33a05e5cf6d9d9bc012226a18c09f33f662fefd99840a63 F ext/wasm/api/sqlite3-api-worker1.js 2eeb2a24e1a90322d84a9b88a99919b806623de62792436446099c0988f2030b F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 -F ext/wasm/api/sqlite3-wasm.c 4130e2df9587f4e4c3afc04c3549d682c8a5c0cfe5b22819a0a86edb7f01b9bd +F ext/wasm/api/sqlite3-wasm.c 9401a3f9bd191a410b4f679b7957c6b7e168a68106f52ddeafa1c776d0364e49 F ext/wasm/batch-runner.html 2857a6db7292ac83d1581af865d643fd34235db2df830d10b43b01388c599e04 F ext/wasm/batch-runner.js 6f5b86e0b5519a9a941d9f17ee9c5ecdc63f452f157602fe7fdf87f6275a2b49 F ext/wasm/common/SqliteTestUtil.js 529161a624265ba84271a52db58da022649832fa1c71309fb1e02cc037327a2b @@ -519,7 +519,7 @@ F ext/wasm/speedtest1.html 8ae6ece128151d01f90579de69cfa06f021acdb760735250ef745 F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 -F ext/wasm/sqlite3-opfs-async-proxy.js 9305d92f32d02983c4528b9c801096cfd8295ca7d24e357d90de9bbcb201d035 +F ext/wasm/sqlite3-opfs-async-proxy.js 0523e3093df2ad2c58691aa65c5e32c0aafb1bbabb6119dd9406d34a8e16dd68 F ext/wasm/sqlite3-worker1-promiser.js 4fd0465688a28a75f1d4ee4406540ba494f49844e3cad0670d0437a001943365 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/test-opfs-vfs.html eb69dda21eb414b8f5e3f7c1cc0f774103cc9c0f87b2d28a33419e778abfbab5 @@ -2026,8 +2026,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 96c734c07acfbea153d9aaf293a28a2d49d78de19fec4fb90c4c08fb1fd0ddbd -R 861bbf4cfa9812be3b9491caf1943540 +P 777077c4c2249e1ec78390d4f65aaf281c1fbefcef4bcc7609199e995645ceb6 +R 52ae18958684abb42f4ad80857c37c9c U stephan -Z 172073e3d6aad2306bf7614651c7edbb +Z 4853fab0e74bf411129cdad2e17be435 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 17b72d2509..c688d57d99 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -777077c4c2249e1ec78390d4f65aaf281c1fbefcef4bcc7609199e995645ceb6 \ No newline at end of file +5c5e80652825cf883e6c17809cb98f2bf17d5feac2d263f6f492479154730dab \ No newline at end of file From e8afca3f16c7c2a68e4f18a615f0efc0d3203c1b Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 21 Sep 2022 14:02:47 +0000 Subject: [PATCH 154/428] Correct mistyped --shrink-memory flag in speedtest1-worker. Minor OPFS proxy cleanups. FossilOrigin-Name: 86e2b55ec9483fa5add51a479c6509d73461f1ac6fca5d49e057b1c66f4314d2 --- ext/wasm/api/sqlite3-api-opfs.js | 56 ++++++++++++++++------------ ext/wasm/speedtest1-worker.html | 2 +- ext/wasm/sqlite3-opfs-async-proxy.js | 24 +++++++----- manifest | 16 ++++---- manifest.uuid | 2 +- 5 files changed, 58 insertions(+), 42 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-opfs.js b/ext/wasm/api/sqlite3-api-opfs.js index a117d2725e..abdf90d49b 100644 --- a/ext/wasm/api/sqlite3-api-opfs.js +++ b/ext/wasm/api/sqlite3-api-opfs.js @@ -99,7 +99,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) options.proxyUri = callee.defaultProxyUri; } - const thePromise = new Promise(function(promiseResolve, promiseReject){ + const thePromise = new Promise(function(promiseResolve, promiseReject_){ const loggers = { 0:console.error.bind(console), 1:console.warn.bind(console), @@ -118,13 +118,6 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) const sqlite3_vfs = capi.sqlite3_vfs; const sqlite3_file = capi.sqlite3_file; const sqlite3_io_methods = capi.sqlite3_io_methods; - const W = new Worker(options.proxyUri); - W._originalOnError = W.onerror /* will be restored later */; - W.onerror = function(err){ - // The error object doesn't contain any useful info when the - // failure is, e.g., that the remote script is 404. - promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons.")); - }; /** Generic utilities for working with OPFS. This will get filled out by the Promise setup and, on success, installed as sqlite3.opfs. @@ -200,6 +193,18 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) gets called at all in a wasm build, which is undefined). */ + const promiseReject = function(err){ + opfsVfs.dispose(); + return promiseReject_(err); + }; + const W = new Worker(options.proxyUri); + W._originalOnError = W.onerror /* will be restored later */; + W.onerror = function(err){ + // The error object doesn't contain any useful info when the + // failure is, e.g., that the remote script is 404. + promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons.")); + }; + /** State which we send to the async-api Worker or share with it. This object must initially contain only cloneable or sharable @@ -220,15 +225,20 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) require a bit of acrobatics but should be feasible. */ const state = Object.create(null); - state.littleEndian = true; state.verbose = options.verbose; + state.littleEndian = true; + /** If true, the async counterpart should log exceptions to + the serialization channel. That produces a great deal of + noise for seemingly innocuous things like xAccess() checks + for missing files. */ + state.asyncS11nExceptions = false; /* Size of file I/O buffer block. 64k = max sqlite3 page size. */ state.fileBufferSize = 1024 * 64; state.sabS11nOffset = state.fileBufferSize; /** The size of the block in our SAB for serializing arguments and - result values. Need to be large enough to hold serialized + result values. Needs to be large enough to hold serialized values of any of the proxied APIs. Filenames are the largest part but are limited to opfsVfs.$mxPathname bytes. */ @@ -278,7 +288,6 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) counterpart... */ state.sq3Codes = Object.create(null); - state.sq3Codes._reverse = Object.create(null); [ 'SQLITE_ERROR', 'SQLITE_IOERR', 'SQLITE_NOTFOUND', 'SQLITE_MISUSE', @@ -291,11 +300,8 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) 'SQLITE_OPEN_READONLY' ].forEach(function(k){ state.sq3Codes[k] = capi[k] || toss("Maintenance required: not found:",k); - state.sq3Codes._reverse[capi[k]] = k; }); - const isWorkerErrCode = (n)=>!!state.sq3Codes._reverse[n]; - /** Runs the given operation (by name) in the async worker counterpart, waits for its response, and returns the result @@ -312,11 +318,11 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) const t = performance.now(); Atomics.wait(state.sabOPView, state.opIds.rc, -1); const rc = Atomics.load(state.sabOPView, state.opIds.rc); - if(rc){ + metrics[op].wait += performance.now() - t; + if(rc && state.asyncS11nExceptions){ const err = state.s11n.deserialize(); if(err) error(op+"() async error:",...err); } - metrics[op].wait += performance.now() - t; return rc; }; @@ -621,7 +627,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) xFileSize: function(pFile,pSz64){ mTimeStart('xFileSize'); const rc = opRun('xFileSize', pFile); - if(!isWorkerErrCode(rc)){ + if(0==rc){ const sz = state.s11n.deserialize()[0]; wasm.setMemValue(pSz64, BigInt(sz), 'i64'); } @@ -882,6 +888,10 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) const dbFile = "/sanity/check/file"+randomFilename(8); const zDbFile = wasm.scopedAllocCString(dbFile); let rc; + state.s11n.serialize("This is ä string."); + rc = state.s11n.deserialize(); + log("deserialize() says:",rc); + if("This is ä string."!==rc[0]) toss("String d13n error."); vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut); rc = wasm.getMemValue(pOut,'i32'); log("xAccess(",dbFile,") exists ?=",rc); @@ -889,7 +899,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) fid, openFlags, pOut); log("open rc =",rc,"state.sabOPView[xOpen] =", state.sabOPView[state.opIds.xOpen]); - if(isWorkerErrCode(rc)){ + if(0!==rc){ error("open failed with code",rc); return; } @@ -936,17 +946,17 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri) //log("Worker.onmessage:",data); switch(data.type){ case 'opfs-async-loaded': - /*Pass our config and shared state on to the async worker.*/ + /*Arrives as soon as the asyc proxy finishes loading. + Pass our config and shared state on to the async worker.*/ W.postMessage({type: 'opfs-async-init',args: state}); break; case 'opfs-async-inited':{ - /*Indicates that the async partner has received the 'init', - so we now know that the state object is no longer subject to - being copied by a pending postMessage() call.*/ + /*Indicates that the async partner has received the 'init' + and has finished initializing, so the real work can + begin...*/ try { const rc = capi.sqlite3_vfs_register(opfsVfs.pointer, 0); if(rc){ - opfsVfs.dispose(); toss("sqlite3_vfs_register(OPFS) failed with rc",rc); } if(opfsVfs.pointer !== capi.sqlite3_vfs_find("opfs")){ diff --git a/ext/wasm/speedtest1-worker.html b/ext/wasm/speedtest1-worker.html index bcbd473422..b1bc3aace5 100644 --- a/ext/wasm/speedtest1-worker.html +++ b/ext/wasm/speedtest1-worker.html @@ -241,7 +241,7 @@ flags["--serialized"] = "Set serialized threading mode"; flags["--singlethread"] = "Set single-threaded mode - disables all mutexing"; flags["--sqlonly"] = "No-op. Only show the SQL that would have been run."; - flags["--shrink"] = "memory Invoke sqlite3_db_release_memory() frequently."; + flags["--shrink-memory"] = "Invoke sqlite3_db_release_memory() frequently."; //flags["--size"] = "N Relative test size. Default=100"; flags["--strict"] = "Use STRICT table where appropriate"; flags["--stats"] = "Show statistics at the end"; diff --git a/ext/wasm/sqlite3-opfs-async-proxy.js b/ext/wasm/sqlite3-opfs-async-proxy.js index 06bc235ab2..6c0765c63d 100644 --- a/ext/wasm/sqlite3-opfs-async-proxy.js +++ b/ext/wasm/sqlite3-opfs-async-proxy.js @@ -190,7 +190,7 @@ const vfsAsyncImpls = { try { await getDirForPath(dirname+"/filepart", true); }catch(e){ - state.s11n.serialize(e.message); + state.s11n.storeException(e); rc = state.sq3Codes.SQLITE_IOERR; }finally{ wTimeEnd(); @@ -217,7 +217,7 @@ const vfsAsyncImpls = { const [dh, fn] = await getDirForPath(filename); await dh.getFileHandle(fn); }catch(e){ - state.s11n.serialize(e.message); + state.s11n.storeException(e); rc = state.sq3Codes.SQLITE_IOERR; }finally{ wTimeEnd(); @@ -278,7 +278,7 @@ const vfsAsyncImpls = { filename = filename.join('/'); } }catch(e){ - state.s11n.serialize(e.message); + state.s11n.storeException(e); rc = state.sq3Codes.SQLITE_IOERR_DELETE; } wTimeEnd(); @@ -294,7 +294,7 @@ const vfsAsyncImpls = { state.s11n.serialize(Number(sz)); sz = 0; }catch(e){ - state.s11n.serialize(e.message); + state.s11n.storeException(e); sz = state.sq3Codes.SQLITE_IOERR; } wTimeEnd(); @@ -339,7 +339,7 @@ const vfsAsyncImpls = { }catch(e){ wTimeEnd(); error(opName,e); - state.s11n.serialize(e.message); + state.s11n.storeException(e); storeAndNotify(opName, state.sq3Codes.SQLITE_IOERR); } mTimeEnd(); @@ -361,7 +361,7 @@ const vfsAsyncImpls = { } }catch(e){ error("xRead() failed",e,fh); - state.s11n.serialize(e.message); + state.s11n.storeException(e); rc = state.sq3Codes.SQLITE_IOERR_READ; } storeAndNotify('xRead',rc); @@ -376,7 +376,7 @@ const vfsAsyncImpls = { wTimeStart('xSync'); await fh.accessHandle.flush(); }catch(e){ - state.s11n.serialize(e.message); + state.s11n.storeException(e); }finally{ wTimeEnd(); } @@ -394,7 +394,7 @@ const vfsAsyncImpls = { await fh.accessHandle.truncate(size); }catch(e){ error("xTruncate():",e,fh); - state.s11n.serialize(e.message); + state.s11n.storeException(e); rc = state.sq3Codes.SQLITE_IOERR_TRUNCATE; } wTimeEnd(); @@ -414,7 +414,7 @@ const vfsAsyncImpls = { ) ? 0 : state.sq3Codes.SQLITE_IOERR_WRITE; }catch(e){ error("xWrite():",e,fh); - state.s11n.serialize(e.message); + state.s11n.storeException(e); rc = state.sq3Codes.SQLITE_IOERR_WRITE; }finally{ wTimeEnd(); @@ -517,6 +517,11 @@ const initS11n = ()=>{ } metrics.s11n.serialize.time += performance.now() - t; }; + + state.s11n.storeException = state.asyncS11nExceptions + ? ((e)=>state.s11n.serialize(e.message)) + : ()=>{}; + return state.s11n; }/*initS11n()*/; @@ -570,6 +575,7 @@ navigator.storage.getDirectory().then(function(d){ /* Receive shared state from synchronous partner */ const opt = data.args; state.littleEndian = opt.littleEndian; + state.asyncS11nExceptions = opt.asyncS11nExceptions; state.verbose = opt.verbose ?? 2; state.fileBufferSize = opt.fileBufferSize; state.sabS11nOffset = opt.sabS11nOffset; diff --git a/manifest b/manifest index d30df61098..4c5eb3c7e3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Doc\scleanups\sand\sadditions.\sAdd\sa\sway\sfor\sthe\sOPFS\sasync\srunner\sto\spropagate\sexception\stext\sto\sthe\scalling\sthread. -D 2022-09-21T12:27:35.940 +C Correct\smistyped\s--shrink-memory\sflag\sin\sspeedtest1-worker.\sMinor\sOPFS\sproxy\scleanups. +D 2022-09-21T14:02:47.634 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -484,7 +484,7 @@ F ext/wasm/api/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1 F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 F ext/wasm/api/sqlite3-api-oo1.js f974e79d9af8f26bf33928c5730b0988cc706d14f59a5fe36394739b92249841 -F ext/wasm/api/sqlite3-api-opfs.js dbbce38b0cd89d1eaf829546e2999241127150a40ff2e0331d842a1f31c756e5 +F ext/wasm/api/sqlite3-api-opfs.js d623ea3519cd81fe18e243adfdd07cd1fa4b07ff3b0fd0d2b269beb0e127acb3 F ext/wasm/api/sqlite3-api-prologue.js 6f3a67c4db37e884d33a05e5cf6d9d9bc012226a18c09f33f662fefd99840a63 F ext/wasm/api/sqlite3-api-worker1.js 2eeb2a24e1a90322d84a9b88a99919b806623de62792436446099c0988f2030b F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 @@ -513,13 +513,13 @@ F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e5 F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06 F ext/wasm/scratchpad-wasmfs-main.js f0836e3576df7a89390d777bb53e142e559e8a79becfb2a5a976490b05a1c4fa F ext/wasm/speedtest1-wasmfs.html 9d8cd19eab8854d17f7129aa11607cae6f6d9857c505a4aef13000588583d93e -F ext/wasm/speedtest1-worker.html ede59f2c1884bf72e3d650064604b48703c81848250b19b8063d260aa3a2201d +F ext/wasm/speedtest1-worker.html 802cbd09a14b5a7abb4b66bff243d5fd89fd476ffe083554bdfd4c177fb6474c F ext/wasm/speedtest1-worker.js 11e7f68cedd2a83b0e638f94c1d2f58406ba672a7e88b66bff5d4f4284e8ba16 F ext/wasm/speedtest1.html 8ae6ece128151d01f90579de69cfa06f021acdb760735250ef745eba7ed05d49 F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 -F ext/wasm/sqlite3-opfs-async-proxy.js 0523e3093df2ad2c58691aa65c5e32c0aafb1bbabb6119dd9406d34a8e16dd68 +F ext/wasm/sqlite3-opfs-async-proxy.js 483b6326e268589ed3749a4d1a60b4ec2afa8ff7c218a09d39430e3bde89862e F ext/wasm/sqlite3-worker1-promiser.js 4fd0465688a28a75f1d4ee4406540ba494f49844e3cad0670d0437a001943365 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e F ext/wasm/test-opfs-vfs.html eb69dda21eb414b8f5e3f7c1cc0f774103cc9c0f87b2d28a33419e778abfbab5 @@ -2026,8 +2026,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 777077c4c2249e1ec78390d4f65aaf281c1fbefcef4bcc7609199e995645ceb6 -R 52ae18958684abb42f4ad80857c37c9c +P 5c5e80652825cf883e6c17809cb98f2bf17d5feac2d263f6f492479154730dab +R 47f6cfb5732f21f9dd8243259a29763d U stephan -Z 4853fab0e74bf411129cdad2e17be435 +Z efa6548d913df45daccab7f9511bade7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c688d57d99..21f4cc3619 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5c5e80652825cf883e6c17809cb98f2bf17d5feac2d263f6f492479154730dab \ No newline at end of file +86e2b55ec9483fa5add51a479c6509d73461f1ac6fca5d49e057b1c66f4314d2 \ No newline at end of file From 65022716b349c82870f13a9004dc6b1071ddfe7d Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 21 Sep 2022 16:21:21 +0000 Subject: [PATCH 155/428] shell.c.in: when building in fiddle mode, define _POSIX_SOURCE (ifndef) so that emcc's string.h reveals strdup(). FossilOrigin-Name: fb85b269c43147f153977606dd8ede7b93744bf955e4c1a2b198907fd8a94620 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/shell.c.in | 8 ++++++++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 4c5eb3c7e3..da44ff671f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Correct\smistyped\s--shrink-memory\sflag\sin\sspeedtest1-worker.\sMinor\sOPFS\sproxy\scleanups. -D 2022-09-21T14:02:47.634 +C shell.c.in:\swhen\sbuilding\sin\sfiddle\smode,\sdefine\s_POSIX_SOURCE\s(ifndef)\sso\sthat\semcc's\sstring.h\sreveals\sstrdup(). +D 2022-09-21T16:21:21.314 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -615,7 +615,7 @@ F src/random.c 546d6feb15ec69c1aafe9bb351a277cbb498fd5410e646add673acb805714960 F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c d69dfb5b082f9a25e6700e152ddb3d942359b847b1df504eb09f9b4531844f8d -F src/shell.c.in e7e7c2c69ae86c5ee9e8ad66227203d46ff6dce8700a1b1dababff01c71d33df +F src/shell.c.in fd57bd685265eda436f98b06adca63c6a6cc170e12c7cd9b0c44826ea65cc6be F src/sqlite.h.in b9b7fd73239d94db20332bb6e504688001e5564b655e1318a4427a1caef4b99e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h a988810c9b21c0dc36dc7a62735012339dc76fc7ab448fb0792721d30eacb69d @@ -2026,8 +2026,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 5c5e80652825cf883e6c17809cb98f2bf17d5feac2d263f6f492479154730dab -R 47f6cfb5732f21f9dd8243259a29763d +P 86e2b55ec9483fa5add51a479c6509d73461f1ac6fca5d49e057b1c66f4314d2 +R e68dc13d6d9761750c3d366ba59d0406 U stephan -Z efa6548d913df45daccab7f9511bade7 +Z b0eba7fb6b60a68e1a0205556e35b01a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 21f4cc3619..398f422236 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -86e2b55ec9483fa5add51a479c6509d73461f1ac6fca5d49e057b1c66f4314d2 \ No newline at end of file +fb85b269c43147f153977606dd8ede7b93744bf955e4c1a2b198907fd8a94620 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index d6f3a0aeb6..bef30f5dfc 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -85,6 +85,14 @@ # define _LARGEFILE_SOURCE 1 #endif +#if defined(SQLITE_SHELL_FIDDLE) && !defined(_POSIX_SOURCE) +/* +** emcc requires _POSIX_SOURCE (or one of several similar defines) +** to expose strdup(). +*/ +# define _POSIX_SOURCE +#endif + #include #include #include From 6110a5d0ab229b40627d09b4118801fca71bc941 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 21 Sep 2022 19:51:25 +0000 Subject: [PATCH 156/428] Put pieces in place for fiddle to support opfs, but more cleanup is required in the sqlite3.js/fiddle connection. bigIntEnabled now defaults to whether the Emscripten's module has bigint enabled. Add wasm-sensible defaults for several SQLITE_ENABLE/OMIT flags in sqlite3-wasm.c. FossilOrigin-Name: 7c7fb7535e86b3960eea7f29ab7e6d5197c166b4ee64ad4a9bc0749f2869badc --- ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle | 1 + ext/wasm/GNUmakefile | 14 +++----- ext/wasm/api/sqlite3-api-prologue.js | 5 ++- ext/wasm/api/sqlite3-wasm.c | 43 +++++++++++++++++++++--- ext/wasm/fiddle.make | 9 +++-- ext/wasm/fiddle/fiddle-worker.js | 23 ++++++++++--- ext/wasm/fiddle/fiddle.html | 5 --- ext/wasm/fiddle/fiddle.js | 2 +- ext/wasm/wasmfs.make | 4 +-- manifest | 28 +++++++-------- manifest.uuid | 2 +- 11 files changed, 90 insertions(+), 46 deletions(-) diff --git a/ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle b/ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle index af2c48a3f4..5e80f703ae 100644 --- a/ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle +++ b/ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle @@ -12,3 +12,4 @@ stackAlloc stackRestore stackSave stringToUTF8Array +wasmMemory diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 79da723247..83c9ec0ead 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -47,6 +47,7 @@ dir.jacc := jaccwabyt dir.common := common dir.fiddle := fiddle CLEAN_FILES := *~ $(dir.jacc)/*~ $(dir.api)/*~ $(dir.common)/*~ +emcc_enable_bigint ?= 1 sqlite3.c := $(dir.top)/sqlite3.c SQLITE_OPT = \ -DSQLITE_ENABLE_FTS4 \ @@ -65,7 +66,8 @@ SQLITE_OPT = \ -DSQLITE_THREADSAFE=0 \ -DSQLITE_TEMP_STORE=3 \ -DSQLITE_OS_KV_OPTIONAL=1 \ - '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' + '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' \ + -DSQLITE_USE_URI=1 #SQLITE_OPT += -DSQLITE_ENABLE_MEMSYS5 # ^^^ MEMSYS5 is hypothetically useful for non-Emscripten builds but # requires adding more infrastructure and fixing one spot in the @@ -221,11 +223,7 @@ emcc.jsflags += --import-undefined #emcc.jsflags += --unresolved-symbols=import-dynamic --experimental-pic #emcc.jsflags += --experimental-pic --unresolved-symbols=ingore-all --import-undefined #emcc.jsflags += --unresolved-symbols=ignore-all -enable_bigint ?= 1 -ifneq (0,$(enable_bigint)) -emcc.jsflags += -sWASM_BIGINT -endif -emcc.jsflags += -sMEMORY64=0 +emcc.jsflags += -sWASM_BIGINT=$(emcc_enable_bigint) # ^^^^ MEMORY64=1 fails to load, erroring with: # invalid memory limits flags 0x5 # (enable via --experimental-wasm-memory64) @@ -333,9 +331,7 @@ speedtest1-common.eflags += -sDYNAMIC_EXECUTION=0 speedtest1-common.eflags += --minify 0 speedtest1-common.eflags += -sEXPORT_NAME=sqlite3Speedtest1InitModule speedtest1-common.eflags += --post-js=$(post-js.js) -ifneq (0,$(enable_bigint)) -speedtest1-common.eflags += -sWASM_BIGINT -endif +speedtest1-common.eflags += -sWASM_BIGINT=$(emcc_enable_bigint) speedtest1.exit-runtime0 := -sEXIT_RUNTIME=0 speedtest1.exit-runtime1 := -sEXIT_RUNTIME=1 # Re -sEXIT_RUNTIME=1 vs 0: if it's 1 and speedtest1 crashes, we get diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index a671080052..1e2d387a15 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -147,7 +147,10 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( Module: undefined/*needed for some test code, not part of the public API*/, exports: undefined, memory: undefined, - bigIntEnabled: !!self.BigInt64Array, + bigIntEnabled: (()=>{ + if('undefined'!==typeof Module) return !!Module.HEAPU64; + return !!self.BigInt64Array; + })(), allocExportName: 'malloc', deallocExportName: 'free', persistentDirName: '/persistent' diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index 5b127cb743..e1e5be2483 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -1,11 +1,44 @@ /* ** This file requires access to sqlite3.c static state in order to -** implement certain WASM-specific features. Unlike the rest of -** sqlite3.c, this file requires compiling with -std=c99 (or -** equivalent, or a later C version) because it makes use of features -** not available in C89. +** implement certain WASM-specific features, and thus directly +** includes that file. Unlike the rest of sqlite3.c, this file +** requires compiling with -std=c99 (or equivalent, or a later C +** version) because it makes use of features not available in C89. +** +** At it's simplest, to build sqlite3.wasm either place this file +** in the same directory as sqlite3.c/h before compilation or use the +** -I/path flag to tell the compiler where to find both of those +** files, then compile this file. For example: +** +** emcc -o sqlite3.wasm ... -I/path/to/sqlite3-c-and-h sqlite3-wasm.c */ -#include "sqlite3.c" + +#ifndef SQLITE_DEFAULT_UNIX_VFS +# define SQLITE_DEFAULT_UNIX_VFS "unix-none" +#endif +#ifndef SQLITE_OMIT_DEPRECATED +# define SQLITE_OMIT_DEPRECATED +#endif +#ifndef SQLITE_OMIT_LOAD_EXTENSION +# define SQLITE_OMIT_LOAD_EXTENSION +#endif +#ifndef SQLITE_OMIT_SHARED_CACHE +# define SQLITE_OMIT_SHARED_CACHE +#endif +#ifndef SQLITE_OMIT_UTF16 +# define SQLITE_OMIT_UTF16 +#endif +#ifndef SQLITE_OS_KV_OPTIONAL +# define SQLITE_OS_KV_OPTIONAL 1 +#endif +#ifndef SQLITE_TEMP_STORE +# define SQLITE_TEMP_STORE 3 +#endif +#ifndef SQLITE_THREADSAFE +# define SQLITE_THREADSAFE 0 +#endif + +#include "sqlite3.c" /* yes, .c instead of .h. */ /* ** WASM_KEEP is identical to EMSCRIPTEN_KEEPALIVE but is not diff --git a/ext/wasm/fiddle.make b/ext/wasm/fiddle.make index 63a665be5c..87018047a4 100644 --- a/ext/wasm/fiddle.make +++ b/ext/wasm/fiddle.make @@ -32,12 +32,12 @@ fiddle.emcc-flags = \ -sENVIRONMENT=web,worker \ -sMODULARIZE \ -sDYNAMIC_EXECUTION=0 \ + -sWASM_BIGINT=$(emcc_enable_bigint) \ -sEXPORT_NAME=initFiddleModule \ -sEXPORTED_RUNTIME_METHODS=@$(dir.wasm)/EXPORTED_RUNTIME_METHODS.fiddle \ -sEXPORTED_FUNCTIONS=@$(dir.wasm)/EXPORTED_FUNCTIONS.fiddle \ --post-js=$(post-js.js) \ $(SQLITE_OPT) $(SHELL_OPT) \ - -D_POSIX_SOURCE \ -DSQLITE_SHELL_FIDDLE # -D_POSIX_C_SOURCE is needed for strdup() with emcc @@ -52,9 +52,13 @@ fiddle-module.js := $(dir.fiddle)/fiddle-module.js fiddle-module.wasm := $(subst .js,.wasm,$(fiddle-module.js)) fiddle.cs := $(dir.top)/shell.c $(sqlite3-wasm.c) +SOAP.js := sqlite3-opfs-async-proxy.js +$(dir.fiddle)/$(SOAP.js): $(SOAP.js) + cp $< $@ + $(fiddle-module.js): $(MAKEFILE) $(MAKEFILE.fiddle) \ EXPORTED_FUNCTIONS.fiddle EXPORTED_RUNTIME_METHODS.fiddle \ - $(fiddle.cs) $(post-js.js) + $(fiddle.cs) $(post-js.js) $(dir.fiddle)/$(SOAP.js) $(emcc.bin) -o $@ $(fiddle.emcc-flags) $(fiddle.cs) $(maybe-wasm-strip) $(fiddle-module.wasm) gzip < $@ > $@.gz @@ -67,6 +71,7 @@ clean: clean-fiddle clean-fiddle: rm -f $(fiddle-module.js) $(fiddle-module.js).gz \ $(fiddle-module.wasm) $(fiddle-module.wasm).gz \ + $(dir.fiddle)/$(SOAP.js) \ EXPORTED_FUNCTIONS.fiddle .PHONY: fiddle fiddle: $(fiddle-module.js) $(dir.fiddle)/fiddle.js.gz diff --git a/ext/wasm/fiddle/fiddle-worker.js b/ext/wasm/fiddle/fiddle-worker.js index 5bc1391753..b76dcf212e 100644 --- a/ext/wasm/fiddle/fiddle-worker.js +++ b/ext/wasm/fiddle/fiddle-worker.js @@ -102,8 +102,8 @@ }); }; - const stdout = function(){wMsg('stdout', Array.prototype.slice.call(arguments));}; - const stderr = function(){wMsg('stderr', Array.prototype.slice.call(arguments));}; + const stdout = (...args)=>wMsg('stdout', args); + const stderr = (...args)=>wMsg('stderr', args); self.onerror = function(/*message, source, lineno, colno, error*/) { const err = arguments[4]; @@ -288,7 +288,7 @@ } }; - importScripts('fiddle-module.js'); + importScripts('fiddle-module.js'+self.location.search); /** initFiddleModule() is installed via fiddle-module.js due to building with: @@ -296,7 +296,20 @@ emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initFiddleModule */ initFiddleModule(fiddleModule).then(function(thisModule){ - thisModule.fsUnlink = thisModule.cwrap('sqlite3_wasm_vfs_unlink','number',['string']); - wMsg('fiddle-ready'); + const S = thisModule.sqlite3; + const atEnd = ()=>{ + thisModule.fsUnlink = thisModule.cwrap('sqlite3_wasm_vfs_unlink','number',['string']); + wMsg('fiddle-ready'); + }; + if(0){ + S.installOpfsVfs().finally(function(){ + if(S.opfs){ + stdout("OPFS is available."); + } + atEnd(); + }); + }else{ + atEnd(); + } }); })(); diff --git a/ext/wasm/fiddle/fiddle.html b/ext/wasm/fiddle/fiddle.html index 3570b283e3..272f1aca76 100644 --- a/ext/wasm/fiddle/fiddle.html +++ b/ext/wasm/fiddle/fiddle.html @@ -273,11 +273,6 @@
    - diff --git a/ext/wasm/fiddle/fiddle.js b/ext/wasm/fiddle/fiddle.js index ec56dd5930..b971233ef9 100644 --- a/ext/wasm/fiddle/fiddle.js +++ b/ext/wasm/fiddle/fiddle.js @@ -326,7 +326,7 @@ } } - SF.worker = new Worker('fiddle-worker.js'); + SF.worker = new Worker('fiddle-worker.js'+self.location.search); SF.worker.onmessage = (ev)=>SF.runMsgHandlers(ev.data); SF.addMsgHandler(['stdout', 'stderr'], (ev)=>SF.echo(ev.data)); diff --git a/ext/wasm/wasmfs.make b/ext/wasm/wasmfs.make index d65684b637..ccfd9063b4 100644 --- a/ext/wasm/wasmfs.make +++ b/ext/wasm/wasmfs.make @@ -76,9 +76,7 @@ speedtest1-common.eflags += -sEXPORTED_FUNCTIONS=@$(dir.wasm)/EXPORTED_FUNCTIONS #^^^ using ALLOW_MEMORY_GROWTH produces a warning from emcc: # USE_PTHREADS + ALLOW_MEMORY_GROWTH may run non-wasm code slowly, # see https://github.com/WebAssembly/design/issues/1271 [-Wpthreads-mem-growth] -ifneq (0,$(enable_bigint)) -sqlite3-wasmfs.jsflags += -sWASM_BIGINT -endif +sqlite3-wasmfs.jsflags += -sWASM_BIGINT=$(emcc_enable_bigint) $(sqlite3-wasmfs.js): $(sqlite3-wasmfs.wasm.c) $(sqlite3-wasm.c) $(sqlite3-wasmfs.extra.c) \ EXPORTED_FUNCTIONS.api $(sqlite3-wasm.js) $(MAKEFILE) $(MAKEFILE.wasmfs) \ diff --git a/manifest b/manifest index da44ff671f..6c44e891e8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C shell.c.in:\swhen\sbuilding\sin\sfiddle\smode,\sdefine\s_POSIX_SOURCE\s(ifndef)\sso\sthat\semcc's\sstring.h\sreveals\sstrdup(). -D 2022-09-21T16:21:21.314 +C Put\spieces\sin\splace\sfor\sfiddle\sto\ssupport\sopfs,\sbut\smore\scleanup\sis\srequired\sin\sthe\ssqlite3.js/fiddle\sconnection.\sbigIntEnabled\snow\sdefaults\sto\swhether\sthe\sEmscripten's\smodule\shas\sbigint\senabled.\sAdd\swasm-sensible\sdefaults\sfor\sseveral\sSQLITE_ENABLE/OMIT\sflags\sin\ssqlite3-wasm.c. +D 2022-09-21T19:51:25.111 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -473,8 +473,8 @@ F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3 -F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 45b2815443729ed7de33acb4a2716038f8a41005529cd96bac81f7f0d97d1ded +F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle 0e88c8cfc3719e4b7e74980d9da664c709e68acf863e48386cda376edfd3bfb0 +F ext/wasm/GNUmakefile 592baa34fba8dc4e272b19c44e768fc1f66c36f96343bb581f02d2fc663192e0 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 8a724a674bd2089eef9676b434c0ab709da00db33f73a94e4987e90169b1cd14 F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -485,10 +485,10 @@ F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 F ext/wasm/api/sqlite3-api-oo1.js f974e79d9af8f26bf33928c5730b0988cc706d14f59a5fe36394739b92249841 F ext/wasm/api/sqlite3-api-opfs.js d623ea3519cd81fe18e243adfdd07cd1fa4b07ff3b0fd0d2b269beb0e127acb3 -F ext/wasm/api/sqlite3-api-prologue.js 6f3a67c4db37e884d33a05e5cf6d9d9bc012226a18c09f33f662fefd99840a63 +F ext/wasm/api/sqlite3-api-prologue.js 76fcd56005717cf4ef6c57cee4d7679c9a5fa8c060110485c1318f5b548abac8 F ext/wasm/api/sqlite3-api-worker1.js 2eeb2a24e1a90322d84a9b88a99919b806623de62792436446099c0988f2030b F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 -F ext/wasm/api/sqlite3-wasm.c 9401a3f9bd191a410b4f679b7957c6b7e168a68106f52ddeafa1c776d0364e49 +F ext/wasm/api/sqlite3-wasm.c d1c0724136480a459d9dda4b76a665691a172d5cba96729d26d26acf6480bc9b F ext/wasm/batch-runner.html 2857a6db7292ac83d1581af865d643fd34235db2df830d10b43b01388c599e04 F ext/wasm/batch-runner.js 6f5b86e0b5519a9a941d9f17ee9c5ecdc63f452f157602fe7fdf87f6275a2b49 F ext/wasm/common/SqliteTestUtil.js 529161a624265ba84271a52db58da022649832fa1c71309fb1e02cc037327a2b @@ -500,11 +500,11 @@ F ext/wasm/demo-123.html aa281d33b7eefa755f3122b7b5a18f39a42dc5fb69c8879171bf14b F ext/wasm/demo-123.js 234655683e35a4543a23de7b10800d76b0369947b33e089e5613171fa7795afb F ext/wasm/demo-kvvfs1.html 7d4f28873de67f51ac18c584b7d920825139866a96049a49c424d6f5a0ea5e7f F ext/wasm/demo-kvvfs1.js e884ea35022d772c0d1dd884b40011413696438394f605c6cd4808cfb1642a4a -F ext/wasm/fiddle.make 8b1018e8d91b6d3add93813302a4acf2e5693dd9882499e38eccaec73afb1f79 +F ext/wasm/fiddle.make fd56fa21bada6ecbf860686a9a789ebda7cc3d9b60835927000fcb00246ea50f F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f -F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 -F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 -F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 +F ext/wasm/fiddle/fiddle-worker.js 3a258f7c79f36958d8f63bceb3dd178bb2dfc6a802329e9349612ecb1d9fcd09 +F ext/wasm/fiddle/fiddle.html 5daf54e8f3d7777cbb1ca4f93affe28858dbfff25841cb4ab81d694efed28ec2 +F ext/wasm/fiddle/fiddle.js dae246414ebd56ecdc09cf95c43b2d50e1332a331832a27915782c7767f92f45 F ext/wasm/index.html 8b4b7ea052d558262c8466f94326fb455c21049b2d1d3577ed0a5fce15101ba8 F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 @@ -530,7 +530,7 @@ F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d766 F ext/wasm/testing1.js 507001a970fe8a8eb67b6c8d783e1c1daa3db2719f727c4551af29349410e538 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c -F ext/wasm/wasmfs.make b24fa0199ddf9720ae8b0c7751a5dbdb2501c8d7c57ecf8c2e0a80ebf4b2d00b +F ext/wasm/wasmfs.make 52f24bc9c10e404d26bd0b0ee28d450269808a78e359d6ddedc45d31e7b9c29c F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -2026,8 +2026,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 86e2b55ec9483fa5add51a479c6509d73461f1ac6fca5d49e057b1c66f4314d2 -R e68dc13d6d9761750c3d366ba59d0406 +P fb85b269c43147f153977606dd8ede7b93744bf955e4c1a2b198907fd8a94620 +R 353052de95c54515940c8efa7fbb5d76 U stephan -Z b0eba7fb6b60a68e1a0205556e35b01a +Z 6e975524f2561f74f6e19ffa720f7e6c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 398f422236..d64a3ada8c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fb85b269c43147f153977606dd8ede7b93744bf955e4c1a2b198907fd8a94620 \ No newline at end of file +7c7fb7535e86b3960eea7f29ab7e6d5197c166b4ee64ad4a9bc0749f2869badc \ No newline at end of file From f75c0c703697a2f1eea678e92f91982b56ca7cbf Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 21 Sep 2022 20:24:12 +0000 Subject: [PATCH 157/428] Reformulate some JS to work around a buggy/broken code transformation in one of the Emscripten-driven code optimizers. FossilOrigin-Name: e1249369d5ec1c582c280b1f578b35d53637fdf1cbd97c16d5ed95b136b83e56 --- ext/wasm/GNUmakefile | 26 +++++++++++++------------- ext/wasm/api/sqlite3-api-glue.js | 6 ++++-- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 83c9ec0ead..030b9ac5db 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -79,7 +79,7 @@ SQLITE_OPT = \ ifneq (,$(filter release,$(MAKECMDGOALS))) emcc_opt ?= -Oz -g3 -flto else -emcc_opt ?= -O0 -g +emcc_opt ?= -O0 -g3 # ^^^^ build times for -O levels higher than 0 are painful at # dev-time. endif @@ -414,18 +414,18 @@ push-fiddle: $(fiddle_files) .PHONY: o0 o1 o2 o3 os oz o-xtra := #o-xtra += -flto -o0: - $(MAKE) clean; $(MAKE) -e "emcc_opt=-O0 $(o-xtra)" fiddle_opt=-O0 -o1: - $(MAKE) clean; $(MAKE) -e "emcc_opt=-O1 $(o-xtra)" fiddle_opt=-O1 -o2: - $(MAKE) clean; $(MAKE) -e "emcc_opt=-O2 $(o-xtra)" fiddle_opt=-O2 -o3: - $(MAKE) clean; $(MAKE) -e "emcc_opt=-O3 $(o-xtra)" fiddle_opt=-O3 -os: - $(MAKE) clean; $(MAKE) -e "emcc_opt=-Os $(o-xtra)" fiddle_opt=-Os -oz: - $(MAKE) clean; $(MAKE) -e "emcc_opt=-Oz $(o-xtra)" fiddle_opt=-Oz +o0: clean + $(MAKE) -e "emcc_opt=-O0 $(o-xtra)" fiddle_opt=-O0 +o1: clean + $(MAKE) -e "emcc_opt=-O1 $(o-xtra)" fiddle_opt=-O1 +o2: clean + $(MAKE) -e "emcc_opt=-O2 $(o-xtra)" fiddle_opt=-O2 +o3: clean + $(MAKE) -e "emcc_opt=-O3 $(o-xtra)" fiddle_opt=-O3 +os: clean + $(MAKE) -e "emcc_opt=-Os $(o-xtra)" fiddle_opt=-Os +oz: clean + $(MAKE) -e "emcc_opt=-Oz $(o-xtra)" fiddle_opt=-Oz ######################################################################## # Sub-makes... diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index 67f9403548..847bf7bc2c 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -179,8 +179,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ 'openFlags', 'prepareFlags', 'resultCodes', 'syncFlags', 'udfFlags', 'version' ]){ - for(const [k,v] of Object.entries(wasm.ctype[t])){ - capi[k] = v; + for(const e of Object.entries(wasm.ctype[t])){ + // ^^^ [k,v] there triggers a buggy code transormation via one + // of the Emscripten-driven optimizers. + capi[e[0]] = e[1]; } } /* Bind all registered C-side structs... */ diff --git a/manifest b/manifest index 6c44e891e8..e575dac86d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Put\spieces\sin\splace\sfor\sfiddle\sto\ssupport\sopfs,\sbut\smore\scleanup\sis\srequired\sin\sthe\ssqlite3.js/fiddle\sconnection.\sbigIntEnabled\snow\sdefaults\sto\swhether\sthe\sEmscripten's\smodule\shas\sbigint\senabled.\sAdd\swasm-sensible\sdefaults\sfor\sseveral\sSQLITE_ENABLE/OMIT\sflags\sin\ssqlite3-wasm.c. -D 2022-09-21T19:51:25.111 +C Reformulate\ssome\sJS\sto\swork\saround\sa\sbuggy/broken\scode\stransformation\sin\sone\sof\sthe\sEmscripten-driven\scode\soptimizers. +D 2022-09-21T20:24:12.588 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle 0e88c8cfc3719e4b7e74980d9da664c709e68acf863e48386cda376edfd3bfb0 -F ext/wasm/GNUmakefile 592baa34fba8dc4e272b19c44e768fc1f66c36f96343bb581f02d2fc663192e0 +F ext/wasm/GNUmakefile 34a84e30e6b25e24959a8264e9dec020dffa82d96879dc55ad65d3c31c95d3b1 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 8a724a674bd2089eef9676b434c0ab709da00db33f73a94e4987e90169b1cd14 F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -482,7 +482,7 @@ F ext/wasm/api/README.md d876597edd2b9542b6ea031adaaff1c042076fde7b670b1dc6d8a87 F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c F ext/wasm/api/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1ecc5ba9e64ef90a8b F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77 -F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30 +F ext/wasm/api/sqlite3-api-glue.js cfff894bdf98a6c579975d09dd45471b0e3399f08a6f9e44a22646e8403196ed F ext/wasm/api/sqlite3-api-oo1.js f974e79d9af8f26bf33928c5730b0988cc706d14f59a5fe36394739b92249841 F ext/wasm/api/sqlite3-api-opfs.js d623ea3519cd81fe18e243adfdd07cd1fa4b07ff3b0fd0d2b269beb0e127acb3 F ext/wasm/api/sqlite3-api-prologue.js 76fcd56005717cf4ef6c57cee4d7679c9a5fa8c060110485c1318f5b548abac8 @@ -2026,8 +2026,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P fb85b269c43147f153977606dd8ede7b93744bf955e4c1a2b198907fd8a94620 -R 353052de95c54515940c8efa7fbb5d76 +P 7c7fb7535e86b3960eea7f29ab7e6d5197c166b4ee64ad4a9bc0749f2869badc +R 21b4f88ebebc444a2dca81501f78488d U stephan -Z 6e975524f2561f74f6e19ffa720f7e6c +Z de96dd791c0a9ecdd14104957785e773 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d64a3ada8c..28b4a319ca 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7c7fb7535e86b3960eea7f29ab7e6d5197c166b4ee64ad4a9bc0749f2869badc \ No newline at end of file +e1249369d5ec1c582c280b1f578b35d53637fdf1cbd97c16d5ed95b136b83e56 \ No newline at end of file From 80b1f6fcde2a2be6219f4e238b0199daf3ad1923 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 23 Sep 2022 11:40:43 +0000 Subject: [PATCH 158/428] Fix various compiler warnings in new code on this branch. FossilOrigin-Name: ae49e9efde3012158061def6e0a8d993abbc5933514a21f84bc10f700f61b5e2 --- ext/recover/sqlite3recover.c | 10 +--------- manifest | 14 +++++++------- manifest.uuid | 2 +- src/shell.c.in | 2 +- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index a370ca0c46..9993aa42ac 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -526,12 +526,8 @@ static void recoverPageIsUsed( ){ sqlite3_recover *p = (sqlite3_recover*)sqlite3_user_data(pCtx); i64 pgno = sqlite3_value_int64(apArg[0]); - sqlite3_stmt *pStmt = 0; - int bRet; - assert( nArg==1 ); - bRet = recoverBitmapQuery(p->pUsed, pgno); - sqlite3_result_int(pCtx, bRet); + sqlite3_result_int(pCtx, recoverBitmapQuery(p->pUsed, pgno)); } /* @@ -572,7 +568,6 @@ static void recoverGetPage( } if( pStmt ){ - int rc = SQLITE_OK; sqlite3_bind_int64(pStmt, 1, pgno); if( SQLITE_ROW==sqlite3_step(pStmt) ){ sqlite3_result_value(pCtx, sqlite3_column_value(pStmt, 0)); @@ -1039,7 +1034,6 @@ static int recoverWriteSchema2(sqlite3_recover *p){ if( pSelect ){ while( sqlite3_step(pSelect)==SQLITE_ROW ){ - i64 iRoot = sqlite3_column_int64(pSelect, 0); const char *zSql = (const char*)sqlite3_column_text(pSelect, 1); int rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0); if( rc==SQLITE_OK ){ @@ -1557,8 +1551,6 @@ static int recoverWriteData(sqlite3_recover *p){ if( bNewCell ){ if( nVal>=0 ){ - int iVal = 0; - if( pInsert==0 || nVal!=nInsert ){ recoverFinalize(p, pInsert); pInsert = recoverInsertStmt(p, pTab, nVal); diff --git a/manifest b/manifest index 92d350697a..3e97238582 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\slatest\strunk\schanges\sinto\sthis\sbranch. -D 2022-09-23T11:30:24.654 +C Fix\svarious\scompiler\swarnings\sin\snew\scode\son\sthis\sbranch. +D 2022-09-23T11:40:43.554 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -397,7 +397,7 @@ F ext/recover/recoverfault2.test 321036336af23e778a87f148c4cc4407f88fbdab1fd72dd F ext/recover/recoverold.test 46e9d99b595fac583d4c67f74d7d89c20a435c752ef6eeb3e918b599940c88e0 F ext/recover/recoverrowid.test 1694a1a5526d825f71279f3d02ab02a1ee4c5265de18858bf54cb8ec54487ac8 F ext/recover/recoversql.test f9872ff2114e13ffd8ee31e1de06919f62b9b48bc080191b5bd076d10becb60f -F ext/recover/sqlite3recover.c 699d14bf128f103f823dbeef0aa4987ae0f8cd822b113fca5be5615ca57f28de +F ext/recover/sqlite3recover.c e4a31b4f1f7a6d18a9c71774049262cd91c3f256f41ee257fbaa7bc71dbd5622 F ext/recover/sqlite3recover.h 81108efb8c4618d3d9c6da4df785212b0e4501aa0d25edfc463405fe839a6640 F ext/recover/test_recover.c 72a765616a3fa9dae2ed537d79b00f365d9f639d347858341b71bda7a3a45f56 F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 @@ -602,7 +602,7 @@ F src/random.c 546d6feb15ec69c1aafe9bb351a277cbb498fd5410e646add673acb805714960 F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c bb18acf4eded647fef88d4d543c673874dbebff516fbeba90a85e6c13f2a58cd -F src/shell.c.in 36d5792e35515f2389f175b41486b78b1ad6e571799e3c9b211f47c3629f96e5 +F src/shell.c.in e3efe4cf89be18c96993b714c3784946ef7af9be682ad8f0e97eb1926510befd F src/sqlite.h.in b9b7fd73239d94db20332bb6e504688001e5564b655e1318a4427a1caef4b99e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h a988810c9b21c0dc36dc7a62735012339dc76fc7ab448fb0792721d30eacb69d @@ -2013,8 +2013,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P abcbb6abfe08fc590123f0aa1bd72cfdecc75908f078f1348dc0e957de98bf52 38aaf26e082bd95df6b64df43e1772fe6e20c4eb71307dcd97559cac7f11f8f1 -R 6b2b96167d95e43311ba941f50355c02 +P e87fa70ab0f9b95bbcde18567f47906a222a3fd02b4f3c2903d2bb087d361b7c +R cddb3dc3765037df525a9b042c70398c U dan -Z abbafc4cd8c328672c6dcbf15b485242 +Z 42c3428e8988c9082d113fe0563e38ab # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 696d9dd5ab..2f79bbe0c8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e87fa70ab0f9b95bbcde18567f47906a222a3fd02b4f3c2903d2bb087d361b7c \ No newline at end of file +ae49e9efde3012158061def6e0a8d993abbc5933514a21f84bc10f700f61b5e2 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 739c2d816c..ba9f0b8eb5 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -7261,7 +7261,7 @@ end_ar_command: */ static int recoverSqlCb(void *pCtx, const char *zSql){ ShellState *pState = (ShellState*)pCtx; - raw_printf(stdout, "%s;\n", zSql); + utf8_printf(pState->out, "%s;\n", zSql); return SQLITE_OK; } From 9bbd91b39dbcf1c6657fa7d2b79d5c9be3137293 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 23 Sep 2022 21:01:10 +0000 Subject: [PATCH 159/428] Internal changes to the recover extension towards a step() style interface. FossilOrigin-Name: 73926d5c8cd1ecece134b5a73b44ee1dfca74dc200606e3f009b06cdecf8cee9 --- ext/recover/sqlite3recover.c | 344 ++++++++++++++++++++--------------- manifest | 12 +- manifest.uuid | 2 +- 3 files changed, 208 insertions(+), 150 deletions(-) diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index 9993aa42ac..74732faca5 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -140,6 +140,23 @@ struct RecoverBitmap { u32 aElem[1]; /* Array of 32-bit bitmasks */ }; +typedef struct RecoverStateW1 RecoverStateW1; +struct RecoverStateW1 { + sqlite3_stmt *pTbls; + sqlite3_stmt *pSel; + sqlite3_stmt *pInsert; + int nInsert; + + RecoverTable *pTab; /* Table currently being written */ + int nMax; /* Max column count in any schema table */ + sqlite3_value **apVal; /* Array of nMax values */ + int nVal; /* Number of valid entries in apVal[] */ + int bHaveRowid; + i64 iRowid; + i64 iPrevPage; + int iPrevCell; +}; + /* ** Main recover handle structure. */ @@ -161,14 +178,21 @@ struct sqlite3_recover { int errCode; /* For sqlite3_recover_errcode() */ char *zErrMsg; /* For sqlite3_recover_errmsg() */ + /* Variables used with eState==RECOVER_STATE_WRITING */ + RecoverStateW1 w1; + /* Fields used within sqlite3_recover_run() */ int bRun; /* True once _recover_run() has been called */ sqlite3 *dbOut; /* Output database */ sqlite3_stmt *pGetPage; /* SELECT against input db sqlite_dbdata */ - RecoverTable *pTblList; /* List of tables recovered from schem */ + RecoverTable *pTblList; /* List of tables recovered from schema */ RecoverBitmap *pUsed; /* Used by recoverLostAndFound() */ }; +#define RECOVER_STATE_INIT 0 +#define RECOVER_STATE_WRITING 1 +#define RECOVER_STATE_LOSTANDFOUND 2 + /* ** Default value for SQLITE_RECOVER_ROWIDS (sqlite3_recover.bRecoverRowid). */ @@ -1381,10 +1405,26 @@ static void recoverLostAndFound(sqlite3_recover *p){ int nField = 0; /* Add all pages that are part of any tree in the recoverable part of - ** the input database schema to the bitmap. */ + ** the input database schema to the bitmap. And, if !p->bFreelistCorrupt, + ** add all pages that appear to be part of the freelist to the bitmap. + */ sqlite3_stmt *pStmt = recoverPrepare( p, p->dbOut, - "WITH roots(r) AS (" + "WITH trunk(pgno) AS (" + " SELECT read_i32(getpage(1), 8) AS x WHERE x>0" + " UNION" + " SELECT read_i32(getpage(trunk.pgno), 0) AS x FROM trunk WHERE x>0" + ")," + "trunkdata(pgno, data) AS (" + " SELECT pgno, getpage(pgno) FROM trunk" + ")," + "freelist(data, n, freepgno) AS (" + " SELECT data, min(16384, read_i32(data, 1)-1), pgno FROM trunkdata" + " UNION ALL" + " SELECT data, n-1, read_i32(data, 2+n) FROM freelist WHERE n>=0" + ")," + "" + "roots(r) AS (" " SELECT 1 UNION ALL" " SELECT rootpage FROM recovery.schema WHERE rootpage>0" ")," @@ -1395,37 +1435,16 @@ static void recoverLostAndFound(sqlite3_recover *p){ " WHERE pgno=page" ") " "SELECT page FROM used" + " UNION ALL " + "SELECT freepgno FROM freelist WHERE NOT ?" ); + if( pStmt ) sqlite3_bind_int(pStmt, 1, p->bFreelistCorrupt); while( pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){ i64 iPg = sqlite3_column_int64(pStmt, 0); recoverBitmapSet(pMap, iPg); } recoverFinalize(p, pStmt); - /* Add all pages that appear to be part of the freelist to the bitmap. */ - if( p->bFreelistCorrupt==0 ){ - pStmt = recoverPrepare(p, p->dbOut, - "WITH trunk(pgno) AS (" - " SELECT read_i32(getpage(1), 8) AS x WHERE x>0" - " UNION" - " SELECT read_i32(getpage(trunk.pgno), 0) AS x FROM trunk WHERE x>0" - ")," - "trunkdata(pgno, data) AS (" - " SELECT pgno, getpage(pgno) FROM trunk" - ")," - "freelist(data, n, freepgno) AS (" - " SELECT data, min(16384, read_i32(data, 1)-1), pgno FROM trunkdata" - " UNION ALL" - " SELECT data, n-1, read_i32(data, 2+n) FROM freelist WHERE n>=0" - ")" - "SELECT freepgno FROM freelist" - ); - while( pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){ - i64 iPg = sqlite3_column_int64(pStmt, 0); - recoverBitmapSet(pMap, iPg); - } - recoverFinalize(p, pStmt); - } /* Add an entry for each page not already added to the bitmap to ** the recovery.map table. This loop leaves the "parent" column @@ -1474,38 +1493,31 @@ static void recoverLostAndFound(sqlite3_recover *p){ } } -/* -** For each table in the recovered schema, this function extracts as much -** data as possible from the output database and writes it to the input -** database. Or, if the recover handle is in SQL callback mode, issues -** equivalent callbacks. -** -** It does not recover "orphaned" data into the lost-and-found table. -** See recoverLostAndFound() for that. -*/ -static int recoverWriteData(sqlite3_recover *p){ - RecoverTable *pTbl; - int nMax = 0; - sqlite3_value **apVal = 0; - - sqlite3_stmt *pTbls = 0; - sqlite3_stmt *pSel = 0; +static int recoverWriteDataInit(sqlite3_recover *p){ + RecoverStateW1 *p1 = &p->w1; + RecoverTable *pTbl = 0; + int nByte = 0; /* Figure out the maximum number of columns for any table in the schema */ + assert( p1->nMax==0 ); for(pTbl=p->pTblList; pTbl; pTbl=pTbl->pNext){ - if( pTbl->nCol>nMax ) nMax = pTbl->nCol; + if( pTbl->nCol>p1->nMax ) p1->nMax = pTbl->nCol; } - apVal = (sqlite3_value**)recoverMalloc(p, sizeof(sqlite3_value*) * (nMax+1)); - if( apVal==0 ) return p->errCode; + /* Allocate an array of (sqlite3_value*) in which to accumulate the values + ** that will be written to the output database in a single row. */ + nByte = sizeof(sqlite3_value*) * (p1->nMax+1); + p1->apVal = (sqlite3_value**)recoverMalloc(p, nByte); + if( p1->apVal==0 ) return p->errCode; - pTbls = recoverPrepare(p, p->dbOut, + /* Prepare the SELECT to loop through schema tables (pTbls) and the SELECT + ** to loop through cells that appear to belong to a single table (pSel). */ + p1->pTbls = recoverPrepare(p, p->dbOut, "SELECT rootpage FROM recovery.schema " " WHERE type='table' AND (sql NOT LIKE 'create virtual%')" " ORDER BY (tbl_name='sqlite_sequence') ASC" ); - - pSel = recoverPrepare(p, p->dbOut, + p1->pSel = recoverPrepare(p, p->dbOut, "WITH RECURSIVE pages(page) AS (" " SELECT ?1" " UNION" @@ -1517,114 +1529,160 @@ static int recoverWriteData(sqlite3_recover *p){ "UNION ALL " "SELECT 0, 0, 0, 0" ); - if( pSel ){ - /* The outer loop runs once for each table to recover. */ - while( sqlite3_step(pTbls)==SQLITE_ROW ){ - i64 iRoot = sqlite3_column_int64(pTbls, 0); - RecoverTable *pTab = recoverFindTable(p, iRoot); - if( pTab ){ - int ii; - sqlite3_stmt *pInsert = 0; - int nInsert = -1; - i64 iPrevPage = -1; - int iPrevCell = -1; - int bHaveRowid = 0; /* True if iRowid is valid */ - i64 iRowid = 0; - int nVal = -1; + return p->errCode; +} - if( sqlite3_stricmp("sqlite_sequence", pTab->zTab)==0 ){ - recoverExec(p, p->dbOut, "DELETE FROM sqlite_sequence"); - recoverSqlCallback(p, "DELETE FROM sqlite_sequence"); +/* +** Clean up resources allocated by recoverWriteDataInit() (stuff in +** sqlite3_recover.w1). +*/ +static void recoverWriteDataCleanup(sqlite3_recover *p){ + RecoverStateW1 *p1 = &p->w1; + int ii; + for(ii=0; iinVal; ii++){ + sqlite3_value_free(p1->apVal[ii]); + } + sqlite3_free(p1->apVal); + recoverFinalize(p, p1->pInsert); + recoverFinalize(p, p1->pTbls); + recoverFinalize(p, p1->pSel); +} + +static int recoverWriteDataStep(sqlite3_recover *p){ + RecoverStateW1 *p1 = &p->w1; + sqlite3_stmt *pSel = p1->pSel; + sqlite3_value **apVal = p1->apVal; + + if( p1->pTab==0 ){ + if( sqlite3_step(p1->pTbls)==SQLITE_ROW ){ + i64 iRoot = sqlite3_column_int64(p1->pTbls, 0); + p1->pTab = recoverFindTable(p, iRoot); + + recoverFinalize(p, p1->pInsert); + p1->pInsert = 0; + + /* If this table is unknown, return early. The caller will invoke this + ** function again and it will move on to the next table. */ + if( p1->pTab==0 ) return p->errCode; + + /* If this is the sqlite_sequence table, delete any rows added by + ** earlier INSERT statements on tables with AUTOINCREMENT primary + ** keys before recovering its contents. The p1->pTbls SELECT statement + ** is rigged to deliver "sqlite_sequence" last of all, so we don't + ** worry about it being modified after it is recovered. */ + if( sqlite3_stricmp("sqlite_sequence", p1->pTab->zTab)==0 ){ + recoverExec(p, p->dbOut, "DELETE FROM sqlite_sequence"); + recoverSqlCallback(p, "DELETE FROM sqlite_sequence"); + } + + /* Bind the root page of this table within the original database to + ** SELECT statement p1->pSel. The SELECT statement will then iterate + ** through cells that look like they belong to table pTab. */ + sqlite3_bind_int64(pSel, 1, iRoot); + + p1->nVal = 0; + p1->bHaveRowid = 0; + p1->iPrevPage = -1; + p1->iPrevCell = -1; + }else{ + return SQLITE_DONE; + } + } + assert( p1->pTab ); + + if( p->errCode==SQLITE_OK && sqlite3_step(pSel)==SQLITE_ROW ){ + RecoverTable *pTab = p1->pTab; + + i64 iPage = sqlite3_column_int64(pSel, 0); + int iCell = sqlite3_column_int(pSel, 1); + int iField = sqlite3_column_int(pSel, 2); + sqlite3_value *pVal = sqlite3_column_value(pSel, 3); + int bNewCell = (p1->iPrevPage!=iPage || p1->iPrevCell!=iCell); + + assert( bNewCell==0 || (iField==-1 || iField==0) ); + assert( bNewCell || iField==p1->nVal || p1->nVal==pTab->nCol ); + + if( bNewCell ){ + int ii = 0; + if( p1->nVal>=0 ){ + if( p1->pInsert==0 || p1->nVal!=p1->nInsert ){ + recoverFinalize(p, p1->pInsert); + p1->pInsert = recoverInsertStmt(p, pTab, p1->nVal); + p1->nInsert = p1->nVal; } - - sqlite3_bind_int64(pSel, 1, iRoot); - while( p->errCode==SQLITE_OK && sqlite3_step(pSel)==SQLITE_ROW ){ - i64 iPage = sqlite3_column_int64(pSel, 0); - int iCell = sqlite3_column_int(pSel, 1); - int iField = sqlite3_column_int(pSel, 2); - sqlite3_value *pVal = sqlite3_column_value(pSel, 3); - - int bNewCell = (iPrevPage!=iPage || iPrevCell!=iCell); - assert( bNewCell==0 || (iField==-1 || iField==0) ); - assert( bNewCell || iField==nVal || nVal==pTab->nCol ); - - if( bNewCell ){ - if( nVal>=0 ){ - if( pInsert==0 || nVal!=nInsert ){ - recoverFinalize(p, pInsert); - pInsert = recoverInsertStmt(p, pTab, nVal); - nInsert = nVal; - } - if( nVal>0 ){ - for(ii=0; iinCol; ii++){ - RecoverColumn *pCol = &pTab->aCol[ii]; - int iBind = pCol->iBind; - if( iBind>0 ){ - if( pCol->bIPK ){ - sqlite3_bind_int64(pInsert, iBind, iRowid); - }else if( pCol->iFieldiField]); - } - } - } - if( p->bRecoverRowid && pTab->iRowidBind>0 && bHaveRowid ){ - sqlite3_bind_int64(pInsert, pTab->iRowidBind, iRowid); - } - - if( SQLITE_ROW==sqlite3_step(pInsert) ){ - const char *z = (const char*)sqlite3_column_text(pInsert, 0); - recoverSqlCallback(p, z); - } - recoverReset(p, pInsert); - assert( p->errCode || pInsert ); - if( pInsert ) sqlite3_clear_bindings(pInsert); + if( p1->nVal>0 ){ + sqlite3_stmt *pInsert = p1->pInsert; + for(ii=0; iinCol; ii++){ + RecoverColumn *pCol = &pTab->aCol[ii]; + int iBind = pCol->iBind; + if( iBind>0 ){ + if( pCol->bIPK ){ + sqlite3_bind_int64(pInsert, iBind, p1->iRowid); + }else if( pCol->iFieldnVal ){ + recoverBindValue(p, pInsert, iBind, apVal[pCol->iField]); } } - - for(ii=0; iinCol ){ - assert( apVal[iField]==0 ); - apVal[iField] = sqlite3_value_dup( pVal ); - if( apVal[iField]==0 ){ - recoverError(p, SQLITE_NOMEM, 0); - } - nVal = iField+1; - } - iPrevCell = iCell; - iPrevPage = iPage; + if( p->bRecoverRowid && pTab->iRowidBind>0 && p1->bHaveRowid ){ + sqlite3_bind_int64(pInsert, pTab->iRowidBind, p1->iRowid); } - } - - recoverReset(p, pSel); - recoverFinalize(p, pInsert); - pInsert = 0; - for(ii=0; iierrCode || pInsert ); + if( pInsert ) sqlite3_clear_bindings(pInsert); } } + + for(ii=0; iinVal; ii++){ + sqlite3_value_free(apVal[ii]); + apVal[ii] = 0; + } + p1->nVal = -1; + p1->bHaveRowid = 0; } + if( iPage!=0 ){ + if( iField<0 ){ + p1->iRowid = sqlite3_column_int64(pSel, 3); + assert( p1->nVal==-1 ); + p1->nVal = 0; + p1->bHaveRowid = 1; + }else if( iFieldnCol ){ + assert( apVal[iField]==0 ); + apVal[iField] = sqlite3_value_dup( pVal ); + if( apVal[iField]==0 ){ + recoverError(p, SQLITE_NOMEM, 0); + } + p1->nVal = iField+1; + } + p1->iPrevCell = iCell; + p1->iPrevPage = iPage; + } + }else{ + recoverReset(p, pSel); + p1->pTab = 0; } - recoverFinalize(p, pTbls); - recoverFinalize(p, pSel); + return p->errCode; +} - sqlite3_free(apVal); +/* +** For each table in the recovered schema, this function extracts as much +** data as possible from the output database and writes it to the input +** database. Or, if the recover handle is in SQL callback mode, issues +** equivalent callbacks. +** +** It does not recover "orphaned" data into the lost-and-found table. +** See recoverLostAndFound() for that. +*/ +static int recoverWriteData(sqlite3_recover *p){ + recoverWriteDataInit(p); + while( p->errCode==SQLITE_OK && SQLITE_OK==recoverWriteDataStep(p) ); + recoverWriteDataCleanup(p); return p->errCode; } diff --git a/manifest b/manifest index 3e97238582..7ebf1eb76a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\svarious\scompiler\swarnings\sin\snew\scode\son\sthis\sbranch. -D 2022-09-23T11:40:43.554 +C Internal\schanges\sto\sthe\srecover\sextension\stowards\sa\sstep()\sstyle\sinterface. +D 2022-09-23T21:01:10.829 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -397,7 +397,7 @@ F ext/recover/recoverfault2.test 321036336af23e778a87f148c4cc4407f88fbdab1fd72dd F ext/recover/recoverold.test 46e9d99b595fac583d4c67f74d7d89c20a435c752ef6eeb3e918b599940c88e0 F ext/recover/recoverrowid.test 1694a1a5526d825f71279f3d02ab02a1ee4c5265de18858bf54cb8ec54487ac8 F ext/recover/recoversql.test f9872ff2114e13ffd8ee31e1de06919f62b9b48bc080191b5bd076d10becb60f -F ext/recover/sqlite3recover.c e4a31b4f1f7a6d18a9c71774049262cd91c3f256f41ee257fbaa7bc71dbd5622 +F ext/recover/sqlite3recover.c cfb2e7df6ac7405109fa4f04b9b1fea4dbb704f37985f2f34d5526949734817f F ext/recover/sqlite3recover.h 81108efb8c4618d3d9c6da4df785212b0e4501aa0d25edfc463405fe839a6640 F ext/recover/test_recover.c 72a765616a3fa9dae2ed537d79b00f365d9f639d347858341b71bda7a3a45f56 F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 @@ -2013,8 +2013,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e87fa70ab0f9b95bbcde18567f47906a222a3fd02b4f3c2903d2bb087d361b7c -R cddb3dc3765037df525a9b042c70398c +P ae49e9efde3012158061def6e0a8d993abbc5933514a21f84bc10f700f61b5e2 +R 84e101ee349aff282424e130c05c161d U dan -Z 42c3428e8988c9082d113fe0563e38ab +Z 2ba1da8e5fc1c584cb2093e5d330b18c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2f79bbe0c8..8e7e75e988 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ae49e9efde3012158061def6e0a8d993abbc5933514a21f84bc10f700f61b5e2 \ No newline at end of file +73926d5c8cd1ecece134b5a73b44ee1dfca74dc200606e3f009b06cdecf8cee9 \ No newline at end of file From 60d9aa7c59319948535b0ca4cce99b7f389ec447 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 24 Sep 2022 07:36:45 +0000 Subject: [PATCH 160/428] Refactoring towards getting fiddle to support OPFS as a first-class citizen. Certain operations, e.g. import, export, and unlink, are not OPFS-aware. FossilOrigin-Name: 1b923ed6438d7fef4508936e0c4bc026a368721698b1539961e3fb3140a185cb --- ext/wasm/EXPORTED_FUNCTIONS.fiddle.in | 9 +- ext/wasm/api/sqlite3-api-prologue.js | 9 +- ext/wasm/common/whwasmutil.js | 33 +- ext/wasm/fiddle/fiddle-worker.js | 55 +- ext/wasm/fiddle/fiddle.js | 1351 ++++++++++++------------- manifest | 22 +- manifest.uuid | 2 +- src/shell.c.in | 49 +- 8 files changed, 776 insertions(+), 754 deletions(-) diff --git a/ext/wasm/EXPORTED_FUNCTIONS.fiddle.in b/ext/wasm/EXPORTED_FUNCTIONS.fiddle.in index b96ce4e67c..392e36e842 100644 --- a/ext/wasm/EXPORTED_FUNCTIONS.fiddle.in +++ b/ext/wasm/EXPORTED_FUNCTIONS.fiddle.in @@ -1,7 +1,8 @@ -_fiddle_exec -_fiddle_interrupt -_fiddle_experiment -_fiddle_the_db _fiddle_db_arg _fiddle_db_filename +_fiddle_exec +_fiddle_experiment +_fiddle_interrupt +_fiddle_main _fiddle_reset_db +_fiddle_the_db diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 1e2d387a15..639ad99c68 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -148,7 +148,11 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( exports: undefined, memory: undefined, bigIntEnabled: (()=>{ - if('undefined'!==typeof Module) return !!Module.HEAPU64; + if('undefined'!==typeof Module){ + /* Emscripten module will contain HEAPU64 when build with + -sWASM_BIGINT=1, else it will not. */ + return !!Module.HEAPU64; + } return !!self.BigInt64Array; })(), allocExportName: 'malloc', @@ -771,7 +775,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( return (p && name) ? name.startsWith(p+'/') : false; }; - if(0===capi.wasm.exports.sqlite3_vfs_find(0)){ + // This bit is highly arguable and is incompatible with the fiddle shell. + if(false && 0===capi.wasm.exports.sqlite3_vfs_find(0)){ /* Assume that sqlite3_initialize() has not yet been called. This will be the case in an SQLITE_OS_KV build. */ capi.wasm.exports.sqlite3_initialize(); diff --git a/ext/wasm/common/whwasmutil.js b/ext/wasm/common/whwasmutil.js index 46cdf84bb8..e9ab8c5942 100644 --- a/ext/wasm/common/whwasmutil.js +++ b/ext/wasm/common/whwasmutil.js @@ -1030,6 +1030,22 @@ self.WhWasmUtilInstaller = function(target){ (jstr, returnWithLength=false)=>__allocCStr(jstr, returnWithLength, target.scopedAlloc, 'scopedAllocCString()'); + // impl for allocMainArgv() and scopedAllocMainArgv(). + const __allocMainArgv = function(isScoped, list){ + if(!list.length) toss("Cannot allocate empty array."); + const pList = target[ + isScoped ? 'scopedAlloc' : 'alloc' + ](list.length * target.ptrSizeof); + let i = 0; + list.forEach((e)=>{ + target.setPtrValue(pList + (target.ptrSizeof * i++), + target[ + isScoped ? 'scopedAllocCString' : 'allocCString' + ](""+e)); + }); + return pList; + }; + /** Creates an array, using scopedAlloc(), suitable for passing to a C-level main() routine. The input is a collection with a length @@ -1042,16 +1058,13 @@ self.WhWasmUtilInstaller = function(target){ Throws if list.length is falsy or scopedAllocPush() is not active. */ - target.scopedAllocMainArgv = function(list){ - if(!list.length) toss("Cannot allocate empty array."); - const pList = target.scopedAlloc(list.length * target.ptrSizeof); - let i = 0; - list.forEach((e)=>{ - target.setPtrValue(pList + (target.ptrSizeof * i++), - target.scopedAllocCString(""+e)); - }); - return pList; - }; + target.scopedAllocMainArgv = (list)=>__allocMainArgv(true, list); + + /** + Identical to scopedAllocMainArgv() but uses alloc() instead of + scopedAllocMainArgv + */ + target.allocMainArgv = (list)=>__allocMainArgv(false, list); /** Wraps function call func() in a scopedAllocPush() and diff --git a/ext/wasm/fiddle/fiddle-worker.js b/ext/wasm/fiddle/fiddle-worker.js index b76dcf212e..97774a1019 100644 --- a/ext/wasm/fiddle/fiddle-worker.js +++ b/ext/wasm/fiddle/fiddle-worker.js @@ -104,6 +104,8 @@ const stdout = (...args)=>wMsg('stdout', args); const stderr = (...args)=>wMsg('stderr', args); + + const fixmeOPFS = "(FIXME: won't work with vanilla OPFS.)"; self.onerror = function(/*message, source, lineno, colno, error*/) { const err = arguments[4]; @@ -125,13 +127,43 @@ if(!f._) f._ = fiddleModule.cwrap('fiddle_db_filename', "string", ['string']); return f._(); }, + runMain: function f(){ + if(f.argv) return f.argv.rc; + const dbName = "/fiddle.sqlite3"; + f.argv = [ + 'sqlite3-fiddle.wasm', + '-bail', '-safe', + dbName + /* Reminder: because of how we run fiddle, we have to ensure + that any argv strings passed to its main() are valid until + the wasm environment shuts down. */ + ]; + const capi = fiddleModule.sqlite3.capi; + f.argv.pArgv = capi.wasm.allocMainArgv(f.argv); + f.argv.rc = capi.wasm.exports.fiddle_main( + f.argv.length, f.argv.pArgv + ); + if(f.argv.rc){ + stderr("Fatal error initializing sqlite3 shell."); + fiddleModule.isDead = true; + return false; + } + stdout("SQLite version", capi.sqlite3_libversion(), + capi.sqlite3_sourceid().substr(0,19)); + stdout('Welcome to the "fiddle" shell'); + stdout('Enter ".help" for usage hints.'); + return true; + }, /** 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(!f._){ + if(!this.runMain()) return; + f._ = fiddleModule.cwrap('fiddle_exec', null, ['string']); + } if(fiddleModule.isDead){ stderr("shell module has exit()ed. Cannot run SQL."); return; @@ -140,18 +172,18 @@ try { if(f._running){ stderr('Cannot run multiple commands concurrently.'); - }else{ + }else if(sql){ f._running = true; f._(sql); } - } finally { + }finally{ delete f._running; wMsg('working','end'); } }, resetDb: function f(){ if(!f._) f._ = fiddleModule.cwrap('fiddle_reset_db', null); - stdout("Resetting database."); + stdout("Resetting database.",fixmeOPFS); f._(); stdout("Reset",this.dbFilename()); }, @@ -189,7 +221,7 @@ */ case 'db-export': { const fn = Sqlite3Shell.dbFilename(); - stdout("Exporting",fn+"."); + stdout("Exporting",fn+".",fixmeOPFS); const fn2 = fn ? fn.split(/[/\\]/).pop() : null; try{ if(!fn2) throw new Error("DB appears to be closed."); @@ -298,18 +330,19 @@ initFiddleModule(fiddleModule).then(function(thisModule){ const S = thisModule.sqlite3; const atEnd = ()=>{ - thisModule.fsUnlink = thisModule.cwrap('sqlite3_wasm_vfs_unlink','number',['string']); + thisModule.fsUnlink = function(fn){ + stderr("unlink:",fixmeOPFS); + return thisModule.ccall('sqlite3_wasm_vfs_unlink','number',['string']); + }; wMsg('fiddle-ready'); }; - if(0){ + if(1){ S.installOpfsVfs().finally(function(){ - if(S.opfs){ - stdout("OPFS is available."); - } + if(S.opfs) stdout("OPFS is available."); atEnd(); }); }else{ atEnd(); } - }); + })/*then()*/; })(); diff --git a/ext/wasm/fiddle/fiddle.js b/ext/wasm/fiddle/fiddle.js index b971233ef9..0d8792d55d 100644 --- a/ext/wasm/fiddle/fiddle.js +++ b/ext/wasm/fiddle/fiddle.js @@ -15,195 +15,195 @@ 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'; - /* 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'; + NS = NS||{}; /** - 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. + This module provides a basic wrapper around localStorage + or sessionStorage or a dummy proxy object if neither + of those are available. */ - const SF/*local convenience alias*/ - = window.SqliteFiddle/*canonical name*/ = { - /* Config options. */ - config: { + 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. */ @@ -219,24 +219,24 @@ sideBySide: true, /* 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) { + }, + /** + 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 + 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(' '); @@ -246,483 +246,482 @@ //text = text.replace(/>/g, ">"); //text = text.replace('\n', '
    ', 'g'); if(null===text){/*special case: clear output*/ - f._.value = ''; - return; + f._.value = ''; + return; }else if(this.echo._clearPending){ - delete this.echo._clearPending; - f._.value = ''; + 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; + f._.scrollTop = f._.scrollHeight; } - }, - _msgMap: {}, - /** Adds a worker message handler for messages of the given - type. */ - addMsgHandler: function f(type,callback){ + }, + _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; + 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){ + }, + /** 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.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){ + }, + /** 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){ + }, + /* 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(){ + }, + /** + 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'); + this.wMsg('db-reset'); } return this; - }, - /** Stores this object's config in the browser's storage. */ - storeConfig: function(){ + }, + /** 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]; - } - }); + 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'+self.location.search); - SF.worker.onmessage = (ev)=>SF.runMsgHandlers(ev.data); - SF.addMsgHandler(['stdout', 'stderr'], (ev)=>SF.echo(ev.data)); + SF.worker = new Worker('fiddle-worker.js'+self.location.search); + 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]); - }; + /* 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. */ - } - }); + /** 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(); - }); + /** + 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. + /** + 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