aboutsummaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
Diffstat (limited to 'ext')
-rw-r--r--ext/wasm/api/extern-post-js.c-pp.js2
-rw-r--r--ext/wasm/api/sqlite3-api-prologue.js38
-rw-r--r--ext/wasm/api/sqlite3-vfs-opfs-sahpool.js82
-rw-r--r--ext/wasm/api/sqlite3-vfs-opfs.c-pp.js2
-rw-r--r--ext/wasm/tester1.c-pp.js81
5 files changed, 137 insertions, 68 deletions
diff --git a/ext/wasm/api/extern-post-js.c-pp.js b/ext/wasm/api/extern-post-js.c-pp.js
index fac00370d..bd5225d11 100644
--- a/ext/wasm/api/extern-post-js.c-pp.js
+++ b/ext/wasm/api/extern-post-js.c-pp.js
@@ -59,7 +59,7 @@ const toExportForESM =
initModuleState.sqlite3Dir = li.join('/') + '/';
}
- globalThis.sqlite3InitModule = function ff(...args){
+ globalThis.sqlite3InitModule = async function ff(...args){
//console.warn("Using replaced sqlite3InitModule()",globalThis.location);
return originalInit(...args).then((EmscriptenModule)=>{
if('undefined'!==typeof WorkerGlobalScope &&
diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js
index ac3253670..22ec87ff3 100644
--- a/ext/wasm/api/sqlite3-api-prologue.js
+++ b/ext/wasm/api/sqlite3-api-prologue.js
@@ -1876,26 +1876,28 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
then it must be called by client-level code, which must not use
the library until the returned promise resolves.
- Bug: if called while a prior call is still resolving, the 2nd
- call will resolve prematurely, before the 1st call has finished
- resolving. The current build setup precludes that possibility,
- so it's only a hypothetical problem if/when this function
- ever needs to be invoked by clients.
+ If called multiple times it will return the same promise on
+ subsequent calls. The current build setup precludes that
+ possibility, so it's only a hypothetical problem if/when this
+ function ever needs to be invoked by clients.
In Emscripten-based builds, this function is called
automatically and deleted from this object.
*/
- asyncPostInit: async function(){
+ asyncPostInit: function ff(){
+ if(ff.ready instanceof Promise) return ff.ready;
let lip = sqlite3ApiBootstrap.initializersAsync;
delete sqlite3ApiBootstrap.initializersAsync;
- if(!lip || !lip.length) return Promise.resolve(sqlite3);
+ if(!lip || !lip.length){
+ return ff.ready = Promise.resolve(sqlite3);
+ }
lip = lip.map((f)=>{
- const p = (f instanceof Promise) ? f : f(sqlite3);
- return p.catch((e)=>{
- console.error("an async sqlite3 initializer failed:",e);
- throw e;
- });
+ return (f instanceof Promise) ? f : f(sqlite3);
});
+ const catcher = (e)=>{
+ config.error("an async sqlite3 initializer failed:",e);
+ throw e;
+ };
const postInit = ()=>{
if(!sqlite3.__isUnderTest){
/* Delete references to internal-only APIs which are used by
@@ -1911,16 +1913,16 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
return sqlite3;
};
if(1){
- /* Run all initializers in sequence. The advantage is that it
- allows us to have post-init cleanup defined outside of this
- routine at the end of the list and have it run at a
- well-defined time. */
+ /* Run all initializers in the sequence they were added. The
+ advantage is that it allows us to have post-init cleanup
+ defined outside of this routine at the end of the list and
+ have it run at a well-defined time. */
let p = lip.shift();
while(lip.length) p = p.then(lip.shift());
- return p.then(postInit);
+ return ff.ready = p.then(postInit).catch(catcher);
}else{
/* Run them in an arbitrary order. */
- return Promise.all(lip).then(postInit);
+ return ff.ready = Promise.all(lip).then(postInit).catch(catcher);
}
},
/**
diff --git a/ext/wasm/api/sqlite3-vfs-opfs-sahpool.js b/ext/wasm/api/sqlite3-vfs-opfs-sahpool.js
index 5ab58bef5..1f026c7cc 100644
--- a/ext/wasm/api/sqlite3-vfs-opfs-sahpool.js
+++ b/ext/wasm/api/sqlite3-vfs-opfs-sahpool.js
@@ -52,7 +52,7 @@
is not detected, the VFS is not registered.
*/
'use strict';
-globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){
+globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
const toss = sqlite3.util.toss;
const toss3 = sqlite3.util.toss3;
const initPromises = Object.create(null);
@@ -400,46 +400,47 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){
/**
- A SAHPoolUtil instance is exposed to clients in order to
- manipulate an OpfsSAHPool object without directly exposing that
+ A SAHPoolUtil instance is exposed to clients in order to manipulate an OpfsSAHPool object without directly exposing that
object and allowing for some semantic changes compared to that
class.
+
+ Class docs are in the client-level docs for installOpfsSAHPoolVfs().
*/
class SAHPoolUtil {
+ /* This object's associated OpfsSAHPool. */
+ #p;
constructor(sahPool){
- /* TODO: move the this-to-sahPool mapping into an external
- WeakMap so as to not expose it to downstream clients. */
- this.$p = sahPool;
+ this.#p = sahPool;
this.vfsName = sahPool.vfsName;
}
- addCapacity = async function(n){
- return this.$p.addCapacity(n);
+ async addCapacity(n){
+ return this.#p.addCapacity(n);
}
- reduceCapacity = async function(n){
- return this.$p.reduceCapacity(n);
+ async reduceCapacity(n){
+ return this.#p.reduceCapacity(n);
}
- getCapacity = function(){
- return this.$p.getCapacity(this.$p);
+ getCapacity(){
+ return this.#p.getCapacity(this.#p);
}
- getActiveFileCount = function(){
- return this.$p.getFileCount();
+ getActiveFileCount(){
+ return this.#p.getFileCount();
}
- reserveMinimumCapacity = async function(min){
- const c = this.$p.getCapacity();
- return (c < min) ? this.$p.addCapacity(min - c) : c;
+ async reserveMinimumCapacity(min){
+ const c = this.#p.getCapacity();
+ return (c < min) ? this.#p.addCapacity(min - c) : c;
}
- exportFile = function(name){
- const sah = this.$p.mapFilenameToSAH.get(name) || toss("File not found:",name);
+ exportFile(name){
+ const sah = this.#p.mapFilenameToSAH.get(name) || toss("File not found:",name);
const n = sah.getSize() - HEADER_OFFSET_DATA;
const b = new Uint8Array(n>=0 ? n : 0);
if(n>0) sah.read(b, {at: HEADER_OFFSET_DATA});
return b;
}
- importDb = function(name, bytes){
+ importDb(name, bytes){
const n = bytes.byteLength;
if(n<512 || n%512!=0){
toss("Byte array size is invalid for an SQLite db.");
@@ -450,35 +451,33 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){
toss("Input does not contain an SQLite database header.");
}
}
- const sah = this.$p.mapFilenameToSAH.get(name)
- || this.$p.nextAvailableSAH()
+ const sah = this.#p.mapFilenameToSAH.get(name)
+ || this.#p.nextAvailableSAH()
|| toss("No available handles to import to.");
sah.write(bytes, {at: HEADER_OFFSET_DATA});
- this.$p.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB);
+ this.#p.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB);
}
- wipeFiles = async function(){
- return this.$p.reset(true);
- }
+ async wipeFiles(){return this.#p.reset(true)}
- unlink = function(filename){
- return this.$p.deletePath(filename);
+ unlink(filename){
+ return this.#p.deletePath(filename);
}
- removeVfs = async function(){
- if(!this.$p.cVfs.pointer) return false;
- capi.sqlite3_vfs_unregister(this.$p.cVfs.pointer);
- this.$p.cVfs.dispose();
+ async removeVfs(){
+ if(!this.#p.cVfs.pointer) return false;
+ capi.sqlite3_vfs_unregister(this.#p.cVfs.pointer);
+ this.#p.cVfs.dispose();
try{
- this.$p.releaseAccessHandles();
- if(this.$p.parentDirHandle){
- await this.$p.parentDirHandle.removeEntry(
- this.$p.dirHandle.name, {recursive: true}
+ this.#p.releaseAccessHandles();
+ if(this.#p.parentDirHandle){
+ await this.#p.parentDirHandle.removeEntry(
+ this.#p.dirHandle.name, {recursive: true}
);
- this.$p.dirHandle = this.$p.parentDirHandle = undefined;
+ this.#p.dirHandle = this.#p.parentDirHandle = undefined;
}
}catch(e){
- console.error(this.$p.vfsName,"removeVfs() failed:",e);
+ sqlite3.config.error(this.#p.vfsName,"removeVfs() failed:",e);
/*otherwise ignored - there is no recovery strategy*/
}
return true;
@@ -679,7 +678,7 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){
throw new Error("Just testing rejection.");
}
if(initPromises[vfsName]){
- //console.warn("Returning same OpfsSAHPool result",vfsName,initPromises[vfsName]);
+ console.warn("Returning same OpfsSAHPool result",options,vfsName,initPromises[vfsName]);
return initPromises[vfsName];
}
if(!globalThis.FileSystemHandle ||
@@ -743,6 +742,9 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){
ensues.
*/
return initPromises[vfsName] = apiVersionCheck().then(async function(){
+ if(options.$testThrowInInit){
+ throw options.$testThrowInInit;
+ }
const thePool = new OpfsSAHPool(opfsVfs, options);
return thePool.isReady.then(async()=>{
/**
@@ -1025,4 +1027,4 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async function(sqlite3){
});
}).catch(promiseReject);
}/*installOpfsSAHPoolVfs()*/;
-}/*sqlite3ApiBootstrap.initializersAsync*/);
+}/*sqlite3ApiBootstrap.initializers*/);
diff --git a/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js b/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js
index 35b7b8865..7d313a263 100644
--- a/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js
+++ b/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js
@@ -23,7 +23,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(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 is intended to be called via
- sqlite3ApiBootstrap.initializersAsync or an equivalent mechanism.
+ sqlite3ApiBootstrap.initializers or an equivalent mechanism.
The installed VFS uses the Origin-Private FileSystem API for
all file storage. On error it is rejected with an exception
diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js
index 8b7119866..ed0e5c3c7 100644
--- a/ext/wasm/tester1.c-pp.js
+++ b/ext/wasm/tester1.c-pp.js
@@ -65,6 +65,14 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
const haveWasmCTests = ()=>{
return !!wasm.exports.sqlite3_wasm_test_intptr;
};
+ const hasOpfs = ()=>{
+ return globalThis.FileSystemHandle
+ && globalThis.FileSystemDirectoryHandle
+ && globalThis.FileSystemFileHandle
+ && globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle
+ && navigator?.storage?.getDirectory;
+ };
+
{
const mapToString = (v)=>{
switch(typeof v){
@@ -277,7 +285,14 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
}
}
const tc = TestUtil.counter, now = performance.now();
- await t.test.call(groupState, sqlite3);
+ let rc = t.test.call(groupState, sqlite3);
+ /*if(rc instanceof Promise){
+ rc = rc.catch((e)=>{
+ error("Test failure:",e);
+ throw e;
+ });
+ }*/
+ await rc;
const then = performance.now();
runtime += then - now;
logClass('faded',indent, indent,
@@ -339,6 +354,11 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
T.g = T.addGroup;
T.t = T.addTest;
let capi, wasm/*assigned after module init*/;
+ const sahPoolConfig = {
+ name: 'opfs-sahpool-tester1',
+ clearOnInit: true,
+ initialCapacity: 3
+ };
////////////////////////////////////////////////////////////////////////
// End of infrastructure setup. Now define the tests...
////////////////////////////////////////////////////////////////////////
@@ -1288,7 +1308,6 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
if(1){
const vfsList = capi.sqlite3_js_vfs_list();
T.assert(vfsList.length>1);
- //log("vfsList =",vfsList);
wasm.scopedAllocCall(()=>{
const vfsArg = (v)=>wasm.xWrap.testConvertArg('sqlite3_vfs*',v);
for(const v of vfsList){
@@ -2617,8 +2636,8 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
////////////////////////////////////////////////////////////////////////
T.g('OPFS: Origin-Private File System',
- (sqlite3)=>(sqlite3.opfs
- ? true : "requires Worker thread in a compatible browser"))
+ (sqlite3)=>(sqlite3.capi.sqlite3_vfs_find("opfs")
+ || 'requires "opfs" VFS'))
.t({
name: 'OPFS db sanity checks',
test: async function(sqlite3){
@@ -2738,6 +2757,48 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
;/* end OPFS tests */
////////////////////////////////////////////////////////////////////////
+ T.g('OPFS SyncAccessHandle Pool VFS',
+ (sqlite3)=>(hasOpfs() || "requires OPFS APIs"))
+ .t({
+ name: 'SAH sanity checks',
+ test: async function(sqlite3){
+ T.assert(!sqlite3.capi.sqlite3_vfs_find(sahPoolConfig.name))
+ .assert(sqlite3.capi.sqlite3_js_vfs_list().indexOf(sahPoolConfig.name) < 0)
+ const inst = sqlite3.installOpfsSAHPoolVfs,
+ catcher = (e)=>{
+ error("Cannot load SAH pool VFS.",
+ "This might not be a problem,",
+ "depending on the environment.");
+ return false;
+ };
+ let u1, u2;
+ const P1 = inst(sahPoolConfig).then(u=>u1 = u).catch(catcher),
+ P2 = inst(sahPoolConfig).then(u=>u2 = u).catch(catcher);
+ await Promise.all([P1, P2]);
+ if(!P1) return;
+ T.assert(u1 === u2)
+ .assert(sahPoolConfig.name === u1.vfsName)
+ .assert(sqlite3.capi.sqlite3_vfs_find(sahPoolConfig.name))
+ .assert(u1.getCapacity() === sahPoolConfig.initialCapacity)
+ .assert(5 === (await u2.addCapacity(2)))
+ .assert(sqlite3.capi.sqlite3_js_vfs_list().indexOf(sahPoolConfig.name) >= 0)
+ .assert(true === await u2.removeVfs())
+ .assert(false === await u1.removeVfs())
+ .assert(!sqlite3.capi.sqlite3_vfs_find(sahPoolConfig.name));
+
+ let cErr, u3;
+ const conf2 = JSON.parse(JSON.stringify(sahPoolConfig));
+ conf2.$testThrowInInit = new Error("Testing throwing during init.");
+ conf2.name = sahPoolConfig.name+'-err';
+ const P3 = await inst(conf2).then(u=>u3 = u).catch((e)=>cErr=e);
+ T.assert(P3 === conf2.$testThrowInInit)
+ .assert(cErr === P3)
+ .assert(undefined === u3)
+ .assert(!sqlite3.capi.sqlite3_vfs_find(conf2.name));
+ }
+ }/*OPFS SAH Pool sanity checks*/)
+
+ ////////////////////////////////////////////////////////////////////////
T.g('Hook APIs')
.t({
name: "sqlite3_commit/rollback/update_hook()",
@@ -2942,8 +3003,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
.assert( capi.sqlite3session_enable(pSession, -1) > 0 )
.assert(undefined === db1.selectValue('select a from t where rowid=2'));
}else{
- warn("sqlite3session_enable() tests disabled due to unexpected results.",
- "(Possibly a tester misunderstanding, as opposed to a bug.)");
+ warn("sqlite3session_enable() tests are currently disabled.");
}
let db1Count = db1.selectValue("select count(*) from t");
T.assert( db1Count === (testSessionEnable ? 2 : 3) );
@@ -3088,11 +3148,15 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
globalThis.sqlite3InitModule({
print: log,
printErr: error
- }).then(function(sqlite3){
- //console.log('sqlite3 =',sqlite3);
+ }).then(async function(sqlite3){
log("Done initializing WASM/JS bits. Running tests...");
sqlite3.config.warn("Installing sqlite3 bits as global S for local dev/test purposes.");
globalThis.S = sqlite3;
+ /*await sqlite3.installOpfsSAHPoolVfs(sahPoolConfig)
+ .then((u)=>log("Loaded",u.vfsName,"VFS"))
+ .catch(e=>{
+ log("Cannot install OpfsSAHPool.",e);
+ });*/
capi = sqlite3.capi;
wasm = sqlite3.wasm;
log("sqlite3 version:",capi.sqlite3_libversion(),
@@ -3107,6 +3171,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
}else{
logClass('warning',"sqlite3_wasm_test_...() APIs unavailable.");
}
+ log("registered vfs list =",capi.sqlite3_js_vfs_list());
TestUtil.runTests(sqlite3);
});
})(self);