mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
Further refactoring of opfs-sahpool and start integrating it into tester1.c-pp.js.
FossilOrigin-Name: 91c789234963b660ae900f0738906b28a477993709e286d8125b2f4d6101601c
This commit is contained in:
@ -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 &&
|
||||
|
@ -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);
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
@ -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(filename){
|
||||
return this.#p.deletePath(filename);
|
||||
}
|
||||
|
||||
unlink = function(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*/);
|
||||
|
@ -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
|
||||
|
@ -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){
|
||||
@ -2737,6 +2756,48 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
}/*OPFS util sanity checks*/)
|
||||
;/* 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({
|
||||
@ -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);
|
||||
|
Reference in New Issue
Block a user