mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
Refactor opfs-sahpool to support multiple instances, each with a separate VFS name and directory.
FossilOrigin-Name: d036eaf6ac60c576428db40f015733c5d5425f7d613194fd8d9d4d98659077c4
This commit is contained in:
@ -54,11 +54,33 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||||
const toss = sqlite3.util.toss;
|
const toss = sqlite3.util.toss;
|
||||||
let vfsRegisterResult = undefined;
|
const isPromiseReady = Object.create(null);
|
||||||
/** The PoolUtil object will be the result of the
|
const capi = sqlite3.capi;
|
||||||
resolved Promise. */
|
// Config opts for the VFS...
|
||||||
const PoolUtil = Object.create(null);
|
const SECTOR_SIZE = 4096;
|
||||||
let isPromiseReady;
|
const HEADER_MAX_PATH_SIZE = 512;
|
||||||
|
const HEADER_FLAGS_SIZE = 4;
|
||||||
|
const HEADER_DIGEST_SIZE = 8;
|
||||||
|
const HEADER_CORPUS_SIZE = HEADER_MAX_PATH_SIZE + HEADER_FLAGS_SIZE;
|
||||||
|
const HEADER_OFFSET_FLAGS = HEADER_MAX_PATH_SIZE;
|
||||||
|
const HEADER_OFFSET_DIGEST = HEADER_CORPUS_SIZE;
|
||||||
|
const HEADER_OFFSET_DATA = SECTOR_SIZE;
|
||||||
|
/* Bitmask of file types which may persist across sessions.
|
||||||
|
SQLITE_OPEN_xyz types not listed here may be inadvertently
|
||||||
|
left in OPFS but are treated as transient by this VFS and
|
||||||
|
they will be cleaned up during VFS init. */
|
||||||
|
const PERSISTENT_FILE_TYPES =
|
||||||
|
capi.SQLITE_OPEN_MAIN_DB |
|
||||||
|
capi.SQLITE_OPEN_MAIN_JOURNAL |
|
||||||
|
capi.SQLITE_OPEN_SUPER_JOURNAL |
|
||||||
|
capi.SQLITE_OPEN_WAL /* noting that WAL support is
|
||||||
|
unavailable in the WASM build.*/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns short a string of random alphanumeric characters
|
||||||
|
suitable for use as a random filename.
|
||||||
|
*/
|
||||||
|
const getRandomName = ()=>Math.random().toString(36).slice(2);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
installOpfsSAHPoolVfs() asynchronously initializes the OPFS
|
installOpfsSAHPoolVfs() asynchronously initializes the OPFS
|
||||||
@ -76,9 +98,14 @@ let isPromiseReady;
|
|||||||
registered, the second page would fail to load the VFS
|
registered, the second page would fail to load the VFS
|
||||||
due to OPFS locking errors.
|
due to OPFS locking errors.
|
||||||
|
|
||||||
On calls after the first this function immediately returns a
|
If this function is called more than once with a given "name"
|
||||||
pending, resolved, or rejected Promise, depending on the state
|
option (see below), it will return the same Promise, with one
|
||||||
of the first call's Promise.
|
exception: if called twice in immediate succession, the first will
|
||||||
|
not yet have had time to create its (cached) return Promise and the
|
||||||
|
second call will attempt to re-initialize the VFS, failing. (How to
|
||||||
|
resolve that race is unclear.) Calls for different names will
|
||||||
|
return different Promises which resolve to independent objects and
|
||||||
|
refer to different VFS registrations.
|
||||||
|
|
||||||
On success, the resulting Promise resolves to a utility object
|
On success, the resulting Promise resolves to a utility object
|
||||||
which can be used to query and manipulate the pool. Its API is
|
which can be used to query and manipulate the pool. Its API is
|
||||||
@ -90,34 +117,48 @@ let isPromiseReady;
|
|||||||
|
|
||||||
The options, in alphabetical order:
|
The options, in alphabetical order:
|
||||||
|
|
||||||
- `clearOnInit`: if truthy, contents and filename mapping are
|
- `clearOnInit`: (default=false) if truthy, contents and filename
|
||||||
removed from each SAH it is acquired during initalization of the
|
mapping are removed from each SAH it is acquired during
|
||||||
VFS, leaving the VFS's storage in a pristine state. Use this only
|
initalization of the VFS, leaving the VFS's storage in a pristine
|
||||||
for databases which need not survive a page reload.
|
state. Use this only for databases which need not survive a page
|
||||||
|
reload.
|
||||||
|
|
||||||
- `initialCapacity`: Specifies the default capacity of the
|
- `initialCapacity`: (default=6) Specifies the default capacity of
|
||||||
VFS. This should not be set unduly high because the VFS has to
|
the VFS. This should not be set unduly high because the VFS has
|
||||||
open (and keep open) a file for each entry in the pool. This
|
to open (and keep open) a file for each entry in the pool. This
|
||||||
setting only has an effect when the pool is initially empty. It
|
setting only has an effect when the pool is initially empty. It
|
||||||
does not have any effect if a pool already exists.
|
does not have any effect if a pool already exists.
|
||||||
|
|
||||||
- `directory`: Specifies the OPFS directory name in which to store
|
- `directory`: (default="."+`name`) Specifies the OPFS directory
|
||||||
metadata for the `"opfs-sahpool"` sqlite3_vfs. Only one instance
|
name in which to store metadata for the `"opfs-sahpool"`
|
||||||
of this VFS can be installed per JavaScript engine, and any two
|
sqlite3_vfs. Only one instance of this VFS can be installed per
|
||||||
engines with the same storage directory name will collide with
|
JavaScript engine, and any two engines with the same storage
|
||||||
each other, leading to locking errors and the inability to
|
directory name will collide with each other, leading to locking
|
||||||
register the VFS in the second and subsequent engine. Using a
|
errors and the inability to register the VFS in the second and
|
||||||
different directory name for each application enables different
|
subsequent engine. Using a different directory name for each
|
||||||
engines in the same HTTP origin to co-exist, but their data are
|
application enables different engines in the same HTTP origin to
|
||||||
invisible to each other. Changing this name will effectively
|
co-exist, but their data are invisible to each other. Changing
|
||||||
orphan any databases stored under previous names. The default is
|
this name will effectively orphan any databases stored under
|
||||||
unspecified but descriptive. This option may contain multiple
|
previous names. The default is unspecified but descriptive. This
|
||||||
path elements, e.g. "foo/bar/baz", and they are created
|
option may contain multiple path elements, e.g. "foo/bar/baz",
|
||||||
automatically. In practice there should be no driving need to
|
and they are created automatically. In practice there should be
|
||||||
change this. ACHTUNG: all files in this directory are assumed to
|
no driving need to change this. ACHTUNG: all files in this
|
||||||
be managed by the VFS. Do not place other files in that
|
directory are assumed to be managed by the VFS. Do not place
|
||||||
directory, as they may be deleted or otherwise modified by the
|
other files in that directory, as they may be deleted or
|
||||||
VFS.
|
otherwise modified by the VFS.
|
||||||
|
|
||||||
|
- `name`: (default="opfs-sahpool") sets the name to register this
|
||||||
|
VFS under. Normally this should not be changed, but it is
|
||||||
|
possible to register this VFS under multiple names so long as
|
||||||
|
each has its own separate directory to work from. The storage for
|
||||||
|
each is invisible to all others. The name must be a string
|
||||||
|
compatible with `sqlite3_vfs_register()` and friends and suitable
|
||||||
|
for use in URI-style database file names.
|
||||||
|
|
||||||
|
Achtung: if a custom `name` is provided, a custom `directory`
|
||||||
|
must also be provided if any other instance is registered with
|
||||||
|
the default directory. If no directory is explicitly provided
|
||||||
|
then a directory name is synthesized from the `name` option.
|
||||||
|
|
||||||
|
|
||||||
The API for the utility object passed on by this function's
|
The API for the utility object passed on by this function's
|
||||||
@ -186,7 +227,6 @@ let isPromiseReady;
|
|||||||
certain whether the higher-level directories contain data which
|
certain whether the higher-level directories contain data which
|
||||||
should be removed.
|
should be removed.
|
||||||
|
|
||||||
|
|
||||||
- [async] number reserveMinimumCapacity(min)
|
- [async] number reserveMinimumCapacity(min)
|
||||||
|
|
||||||
If the current capacity is less than `min`, the capacity is
|
If the current capacity is less than `min`, the capacity is
|
||||||
@ -200,6 +240,10 @@ let isPromiseReady;
|
|||||||
effects. Results are undefined if the file is currently in active
|
effects. Results are undefined if the file is currently in active
|
||||||
use.
|
use.
|
||||||
|
|
||||||
|
- string vfsName
|
||||||
|
|
||||||
|
The SQLite VFS name under which this pool's VFS is registered.
|
||||||
|
|
||||||
- [async] void wipeFiles()
|
- [async] void wipeFiles()
|
||||||
|
|
||||||
Clears all client-defined state of all SAHs and makes all of them
|
Clears all client-defined state of all SAHs and makes all of them
|
||||||
@ -208,65 +252,49 @@ let isPromiseReady;
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
sqlite3.installOpfsSAHPoolVfs = async function(options=Object.create(null)){
|
sqlite3.installOpfsSAHPoolVfs = async function(options=Object.create(null)){
|
||||||
if(PoolUtil===vfsRegisterResult) return Promise.resolve(PoolUtil);
|
/** The PoolUtil object will be the result of the
|
||||||
else if(isPromiseReady) return isPromiseReady;
|
resolved Promise. */
|
||||||
else if(undefined!==vfsRegisterResult){
|
const PoolUtil = Object.create(null);
|
||||||
return Promise.reject(vfsRegisterResult);
|
const vfsName = PoolUtil.vfsName = options.name || "opfs-sahpool";
|
||||||
|
if(isPromiseReady[vfsName]){
|
||||||
|
//console.warn("Returning same OpfsSAHPool result",vfsName,isPromiseReady[vfsName]);
|
||||||
|
return isPromiseReady[vfsName];
|
||||||
}
|
}
|
||||||
if(!globalThis.FileSystemHandle ||
|
if(!globalThis.FileSystemHandle ||
|
||||||
!globalThis.FileSystemDirectoryHandle ||
|
!globalThis.FileSystemDirectoryHandle ||
|
||||||
!globalThis.FileSystemFileHandle ||
|
!globalThis.FileSystemFileHandle ||
|
||||||
!globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle ||
|
!globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle ||
|
||||||
!navigator?.storage?.getDirectory){
|
!navigator?.storage?.getDirectory){
|
||||||
return Promise.reject(vfsRegisterResult = new Error("Missing required OPFS APIs."));
|
return (isPromiseReady[vfsName] = Promise.reject(new Error("Missing required OPFS APIs.")));
|
||||||
}
|
}
|
||||||
vfsRegisterResult = new Error("opfs-sahpool initialization still underway.");
|
const verbosity = options.verbosity
|
||||||
const verbosity = 2 /*3+ == everything*/;
|
|| 2 /*3+ == everything, 2 == warnings+errors, 1 == errors only*/;
|
||||||
const loggers = [
|
const loggers = [
|
||||||
sqlite3.config.error,
|
sqlite3.config.error,
|
||||||
sqlite3.config.warn,
|
sqlite3.config.warn,
|
||||||
sqlite3.config.log
|
sqlite3.config.log
|
||||||
];
|
];
|
||||||
const logImpl = (level,...args)=>{
|
const logImpl = (level,...args)=>{
|
||||||
if(verbosity>level) loggers[level]("opfs-sahpool:",...args);
|
if(verbosity>level) loggers[level](vfsName+":",...args);
|
||||||
};
|
};
|
||||||
const log = (...args)=>logImpl(2, ...args);
|
const log = (...args)=>logImpl(2, ...args);
|
||||||
const warn = (...args)=>logImpl(1, ...args);
|
const warn = (...args)=>logImpl(1, ...args);
|
||||||
const error = (...args)=>logImpl(0, ...args);
|
const error = (...args)=>logImpl(0, ...args);
|
||||||
const capi = sqlite3.capi;
|
|
||||||
const wasm = sqlite3.wasm;
|
const wasm = sqlite3.wasm;
|
||||||
const opfsIoMethods = new capi.sqlite3_io_methods();
|
const opfsIoMethods = new capi.sqlite3_io_methods();
|
||||||
const opfsVfs = new capi.sqlite3_vfs()
|
const opfsVfs = new capi.sqlite3_vfs()
|
||||||
.addOnDispose(()=>opfsIoMethods.dispose());
|
.addOnDispose(()=>opfsIoMethods.dispose());
|
||||||
|
|
||||||
const promiseReject = (err)=>{
|
const promiseReject = (err)=>{
|
||||||
error("rejecting promise:",err);
|
error("rejecting promise:",err);
|
||||||
//opfsVfs.dispose();
|
//opfsVfs.dispose();
|
||||||
vfsRegisterResult = err;
|
return isPromiseReady[vfsName] = Promise.reject(err);
|
||||||
return Promise.reject(err);
|
|
||||||
};
|
};
|
||||||
const promiseResolve =
|
if( sqlite3.capi.sqlite3_vfs_find(vfsName)){
|
||||||
()=>Promise.resolve(vfsRegisterResult = PoolUtil);
|
return promiseReject(new Error("VFS name is already registered:",
|
||||||
// Config opts for the VFS...
|
vfsName));
|
||||||
const SECTOR_SIZE = 4096;
|
}
|
||||||
const HEADER_MAX_PATH_SIZE = 512;
|
|
||||||
const HEADER_FLAGS_SIZE = 4;
|
|
||||||
const HEADER_DIGEST_SIZE = 8;
|
|
||||||
const HEADER_CORPUS_SIZE = HEADER_MAX_PATH_SIZE + HEADER_FLAGS_SIZE;
|
|
||||||
const HEADER_OFFSET_FLAGS = HEADER_MAX_PATH_SIZE;
|
|
||||||
const HEADER_OFFSET_DIGEST = HEADER_CORPUS_SIZE;
|
|
||||||
const HEADER_OFFSET_DATA = SECTOR_SIZE;
|
|
||||||
const DEFAULT_CAPACITY =
|
|
||||||
options.initialCapacity || 6;
|
|
||||||
/* Bitmask of file types which may persist across sessions.
|
|
||||||
SQLITE_OPEN_xyz types not listed here may be inadvertently
|
|
||||||
left in OPFS but are treated as transient by this VFS and
|
|
||||||
they will be cleaned up during VFS init. */
|
|
||||||
const PERSISTENT_FILE_TYPES =
|
|
||||||
capi.SQLITE_OPEN_MAIN_DB |
|
|
||||||
capi.SQLITE_OPEN_MAIN_JOURNAL |
|
|
||||||
capi.SQLITE_OPEN_SUPER_JOURNAL |
|
|
||||||
capi.SQLITE_OPEN_WAL /* noting that WAL support is
|
|
||||||
unavailable in the WASM build.*/;
|
|
||||||
/* We fetch the default VFS so that we can inherit some
|
/* We fetch the default VFS so that we can inherit some
|
||||||
methods from it. */
|
methods from it. */
|
||||||
const pDVfs = capi.sqlite3_vfs_find(null);
|
const pDVfs = capi.sqlite3_vfs_find(null);
|
||||||
@ -279,22 +307,16 @@ sqlite3.installOpfsSAHPoolVfs = async function(options=Object.create(null)){
|
|||||||
opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof;
|
opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof;
|
||||||
opfsVfs.$mxPathname = HEADER_MAX_PATH_SIZE;
|
opfsVfs.$mxPathname = HEADER_MAX_PATH_SIZE;
|
||||||
opfsVfs.addOnDispose(
|
opfsVfs.addOnDispose(
|
||||||
opfsVfs.$zName = wasm.allocCString("opfs-sahpool"),
|
opfsVfs.$zName = wasm.allocCString(vfsName),
|
||||||
()=>(dVfs ? dVfs.dispose() : null)
|
()=>(dVfs ? dVfs.dispose() : null)
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
Returns short a string of random alphanumeric characters
|
|
||||||
suitable for use as a random filename.
|
|
||||||
*/
|
|
||||||
const getRandomName = ()=>Math.random().toString(36).slice(2);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
All state for the VFS.
|
All state for the VFS.
|
||||||
*/
|
*/
|
||||||
const SAHPool = Object.assign(Object.create(null),{
|
const SAHPool = Object.assign(Object.create(null),{
|
||||||
/* OPFS dir in which VFS metadata is stored. */
|
/* OPFS dir in which VFS metadata is stored. */
|
||||||
vfsDir: options.directory || ".sqlite3-opfs-sahpool",
|
vfsDir: options.directory || ("."+vfsName),
|
||||||
/* Directory handle to this.vfsDir. */
|
/* Directory handle to this.vfsDir. */
|
||||||
dirHandle: undefined,
|
dirHandle: undefined,
|
||||||
/* Directory handle to this.dirHandle's parent dir. Needed
|
/* Directory handle to this.dirHandle's parent dir. Needed
|
||||||
@ -491,7 +513,7 @@ sqlite3.installOpfsSAHPoolVfs = async function(options=Object.create(null)){
|
|||||||
argument.
|
argument.
|
||||||
*/
|
*/
|
||||||
reset: async function(clearFiles){
|
reset: async function(clearFiles){
|
||||||
await isPromiseReady;
|
await isPromiseReady[vfsName];
|
||||||
let h = await navigator.storage.getDirectory();
|
let h = await navigator.storage.getDirectory();
|
||||||
let prev, prevName;
|
let prev, prevName;
|
||||||
for(const d of this.vfsDir.split('/')){
|
for(const d of this.vfsDir.split('/')){
|
||||||
@ -559,245 +581,6 @@ sqlite3.installOpfsSAHPoolVfs = async function(options=Object.create(null)){
|
|||||||
SAHPool.dvBody =
|
SAHPool.dvBody =
|
||||||
new DataView(SAHPool.apBody.buffer, SAHPool.apBody.byteOffset);
|
new DataView(SAHPool.apBody.buffer, SAHPool.apBody.byteOffset);
|
||||||
//sqlite3.SAHPool = SAHPool/*only for testing*/;
|
//sqlite3.SAHPool = SAHPool/*only for testing*/;
|
||||||
/**
|
|
||||||
Impls for the sqlite3_io_methods methods. Maintenance reminder:
|
|
||||||
members are in alphabetical order to simplify finding them.
|
|
||||||
*/
|
|
||||||
const ioMethods = {
|
|
||||||
xCheckReservedLock: function(pFile,pOut){
|
|
||||||
log('xCheckReservedLock');
|
|
||||||
SAHPool.storeErr();
|
|
||||||
wasm.poke32(pOut, 1);
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
xClose: function(pFile){
|
|
||||||
SAHPool.storeErr();
|
|
||||||
const file = SAHPool.mapIdToFile.get(pFile);
|
|
||||||
if(file) {
|
|
||||||
try{
|
|
||||||
log(`xClose ${file}`);
|
|
||||||
if(file.sq3File) file.sq3File.dispose();
|
|
||||||
file.sah.flush();
|
|
||||||
SAHPool.mapIdToFile.delete(pFile);
|
|
||||||
if(file.flags & capi.SQLITE_OPEN_DELETEONCLOSE){
|
|
||||||
SAHPool.deletePath(file.path);
|
|
||||||
}
|
|
||||||
}catch(e){
|
|
||||||
SAHPool.storeErr(e);
|
|
||||||
return capi.SQLITE_IOERR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
xDeviceCharacteristics: function(pFile){
|
|
||||||
return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
|
|
||||||
},
|
|
||||||
xFileControl: function(pFile, opId, pArg){
|
|
||||||
return capi.SQLITE_NOTFOUND;
|
|
||||||
},
|
|
||||||
xFileSize: function(pFile,pSz64){
|
|
||||||
log(`xFileSize`);
|
|
||||||
const file = SAHPool.mapIdToFile.get(pFile);
|
|
||||||
const size = file.sah.getSize() - HEADER_OFFSET_DATA;
|
|
||||||
//log(`xFileSize ${file.path} ${size}`);
|
|
||||||
wasm.poke64(pSz64, BigInt(size));
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
xLock: function(pFile,lockType){
|
|
||||||
log(`xLock ${lockType}`);
|
|
||||||
SAHPool.storeErr();
|
|
||||||
const file = SAHPool.mapIdToFile.get(pFile);
|
|
||||||
file.lockType = lockType;
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
xRead: function(pFile,pDest,n,offset64){
|
|
||||||
log(`xRead ${n}@${offset64}`);
|
|
||||||
SAHPool.storeErr();
|
|
||||||
const file = SAHPool.mapIdToFile.get(pFile);
|
|
||||||
log(`xRead ${file.path} ${n} ${offset64}`);
|
|
||||||
try {
|
|
||||||
const nRead = file.sah.read(
|
|
||||||
wasm.heap8u().subarray(pDest, pDest+n),
|
|
||||||
{at: HEADER_OFFSET_DATA + Number(offset64)}
|
|
||||||
);
|
|
||||||
if(nRead < n){
|
|
||||||
wasm.heap8u().fill(0, pDest + nRead, pDest + n);
|
|
||||||
return capi.SQLITE_IOERR_SHORT_READ;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
SAHPool.storeErr(e);
|
|
||||||
return capi.SQLITE_IOERR;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xSectorSize: function(pFile){
|
|
||||||
return SECTOR_SIZE;
|
|
||||||
},
|
|
||||||
xSync: function(pFile,flags){
|
|
||||||
log(`xSync ${flags}`);
|
|
||||||
SAHPool.storeErr();
|
|
||||||
const file = SAHPool.mapIdToFile.get(pFile);
|
|
||||||
//log(`xSync ${file.path} ${flags}`);
|
|
||||||
try{
|
|
||||||
file.sah.flush();
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
SAHPool.storeErr(e);
|
|
||||||
return capi.SQLITE_IOERR;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xTruncate: function(pFile,sz64){
|
|
||||||
log(`xTruncate ${sz64}`);
|
|
||||||
SAHPool.storeErr();
|
|
||||||
const file = SAHPool.mapIdToFile.get(pFile);
|
|
||||||
//log(`xTruncate ${file.path} ${iSize}`);
|
|
||||||
try{
|
|
||||||
file.sah.truncate(HEADER_OFFSET_DATA + Number(sz64));
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
SAHPool.storeErr(e);
|
|
||||||
return capi.SQLITE_IOERR;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xUnlock: function(pFile,lockType){
|
|
||||||
log('xUnlock');
|
|
||||||
const file = SAHPool.mapIdToFile.get(pFile);
|
|
||||||
file.lockType = lockType;
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
xWrite: function(pFile,pSrc,n,offset64){
|
|
||||||
SAHPool.storeErr();
|
|
||||||
const file = SAHPool.mapIdToFile.get(pFile);
|
|
||||||
log(`xWrite ${file.path} ${n} ${offset64}`);
|
|
||||||
try{
|
|
||||||
const nBytes = file.sah.write(
|
|
||||||
wasm.heap8u().subarray(pSrc, pSrc+n),
|
|
||||||
{ at: HEADER_OFFSET_DATA + Number(offset64) }
|
|
||||||
);
|
|
||||||
return nBytes === n ? 0 : capi.SQLITE_IOERR;
|
|
||||||
}catch(e){
|
|
||||||
SAHPool.storeErr(e);
|
|
||||||
return capi.SQLITE_IOERR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}/*ioMethods*/;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Impls for the sqlite3_vfs methods. Maintenance reminder: members
|
|
||||||
are in alphabetical order to simplify finding them.
|
|
||||||
*/
|
|
||||||
const vfsMethods = {
|
|
||||||
xAccess: function(pVfs,zName,flags,pOut){
|
|
||||||
log(`xAccess ${wasm.cstrToJs(zName)}`);
|
|
||||||
SAHPool.storeErr();
|
|
||||||
try{
|
|
||||||
const name = this.getPath(zName);
|
|
||||||
wasm.poke32(pOut, SAHPool.mapFilenameToSAH.has(name) ? 1 : 0);
|
|
||||||
}catch(e){
|
|
||||||
/*ignored*/;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
xCurrentTime: function(pVfs,pOut){
|
|
||||||
wasm.poke(pOut, 2440587.5 + (new Date().getTime()/86400000),
|
|
||||||
'double');
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
xCurrentTimeInt64: function(pVfs,pOut){
|
|
||||||
wasm.poke(pOut, (2440587.5 * 86400000) + new Date().getTime(),
|
|
||||||
'i64');
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
xDelete: function(pVfs, zName, doSyncDir){
|
|
||||||
log(`xDelete ${wasm.cstrToJs(zName)}`);
|
|
||||||
SAHPool.storeErr();
|
|
||||||
try{
|
|
||||||
SAHPool.deletePath(SAHPool.getPath(zName));
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
SAHPool.storeErr(e);
|
|
||||||
return capi.SQLITE_IOERR_DELETE;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xFullPathname: function(pVfs,zName,nOut,pOut){
|
|
||||||
log(`xFullPathname ${wasm.cstrToJs(zName)}`);
|
|
||||||
const i = wasm.cstrncpy(pOut, zName, nOut);
|
|
||||||
return i<nOut ? 0 : capi.SQLITE_CANTOPEN;
|
|
||||||
},
|
|
||||||
xGetLastError: function(pVfs,nOut,pOut){
|
|
||||||
log(`xGetLastError ${nOut}`);
|
|
||||||
const e = SAHPool.popErr();
|
|
||||||
if(e){
|
|
||||||
const scope = wasm.scopedAllocPush();
|
|
||||||
try{
|
|
||||||
const [cMsg, n] = wasm.scopedAllocCString(e.message, true);
|
|
||||||
wasm.cstrncpy(pOut, cMsg, nOut);
|
|
||||||
if(n > nOut) wasm.poke8(pOut + nOut - 1, 0);
|
|
||||||
}catch(e){
|
|
||||||
return capi.SQLITE_NOMEM;
|
|
||||||
}finally{
|
|
||||||
wasm.scopedAllocPop(scope);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
//xSleep is optionally defined below
|
|
||||||
xOpen: function f(pVfs, zName, pFile, flags, pOutFlags){
|
|
||||||
log(`xOpen ${wasm.cstrToJs(zName)} ${flags}`);
|
|
||||||
try{
|
|
||||||
// First try to open a path that already exists in the file system.
|
|
||||||
const path = (zName && wasm.peek8(zName))
|
|
||||||
? SAHPool.getPath(zName)
|
|
||||||
: getRandomName();
|
|
||||||
let sah = SAHPool.mapFilenameToSAH.get(path);
|
|
||||||
if(!sah && (flags & capi.SQLITE_OPEN_CREATE)) {
|
|
||||||
// File not found so try to create it.
|
|
||||||
if(SAHPool.getFileCount() < SAHPool.getCapacity()) {
|
|
||||||
// Choose an unassociated OPFS file from the pool.
|
|
||||||
sah = SAHPool.nextAvailableSAH();
|
|
||||||
SAHPool.setAssociatedPath(sah, path, flags);
|
|
||||||
}else{
|
|
||||||
// File pool is full.
|
|
||||||
toss('SAH pool is full. Cannot create file',path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!sah){
|
|
||||||
toss('file not found:',path);
|
|
||||||
}
|
|
||||||
// Subsequent methods are only passed the file pointer, so
|
|
||||||
// map the relevant info we need to that pointer.
|
|
||||||
const file = {path, flags, sah};
|
|
||||||
SAHPool.mapIdToFile.set(pFile, file);
|
|
||||||
wasm.poke32(pOutFlags, flags);
|
|
||||||
file.sq3File = new capi.sqlite3_file(pFile);
|
|
||||||
file.sq3File.$pMethods = opfsIoMethods.pointer;
|
|
||||||
file.lockType = capi.SQLITE_LOCK_NONE;
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
SAHPool.storeErr(e);
|
|
||||||
return capi.SQLITE_CANTOPEN;
|
|
||||||
}
|
|
||||||
}/*xOpen()*/
|
|
||||||
}/*vfsMethods*/;
|
|
||||||
|
|
||||||
if(dVfs){
|
|
||||||
/* Inherit certain VFS members from the default VFS,
|
|
||||||
if available. */
|
|
||||||
opfsVfs.$xRandomness = dVfs.$xRandomness;
|
|
||||||
opfsVfs.$xSleep = dVfs.$xSleep;
|
|
||||||
}
|
|
||||||
if(!opfsVfs.$xRandomness){
|
|
||||||
/* If the default VFS has no xRandomness(), add a basic JS impl... */
|
|
||||||
vfsMethods.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){
|
|
||||||
vfsMethods.xSleep = (pVfs,ms)=>0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Ensure that the client has a "fully-sync" SAH impl,
|
Ensure that the client has a "fully-sync" SAH impl,
|
||||||
@ -827,71 +610,311 @@ sqlite3.installOpfsSAHPoolVfs = async function(options=Object.create(null)){
|
|||||||
return promiseReject(apiVersionCheck);
|
return promiseReject(apiVersionCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
PoolUtil.$SAHPool = SAHPool/* ONLY for testing and debugging */;
|
return isPromiseReady[vfsName] = SAHPool.reset(!!options.clearOnInit).then(async ()=>{
|
||||||
PoolUtil.addCapacity = async (n)=>SAHPool.addCapacity(n);
|
|
||||||
PoolUtil.reduceCapacity = async (n)=>SAHPool.reduceCapacity(n);
|
|
||||||
PoolUtil.getCapacity = SAHPool.getCapacity.bind(SAHPool);
|
|
||||||
PoolUtil.getActiveFileCount = SAHPool.getFileCount.bind(SAHPool);
|
|
||||||
PoolUtil.reserveMinimumCapacity = async (min)=>{
|
|
||||||
const c = SAHPool.getCapacity();
|
|
||||||
return (c < min) ? SAHPool.addCapacity(min - c) : c;
|
|
||||||
};
|
|
||||||
|
|
||||||
PoolUtil.exportFile = function(name){
|
|
||||||
const sah = SAHPool.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;
|
|
||||||
};
|
|
||||||
|
|
||||||
PoolUtil.importDb = function(name, bytes){
|
|
||||||
const n = bytes.byteLength;
|
|
||||||
if(n<512 || n%512!=0){
|
|
||||||
toss("Byte array size is invalid for an SQLite db.");
|
|
||||||
}
|
|
||||||
const header = "SQLite format 3";
|
|
||||||
for(let i = 0; i < header.length; ++i){
|
|
||||||
if( header.charCodeAt(i) !== bytes[i] ){
|
|
||||||
toss("Input does not contain an SQLite database header.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const sah = SAHPool.mapFilenameToSAH.get(name)
|
|
||||||
|| SAHPool.nextAvailableSAH()
|
|
||||||
|| toss("No available handles to import to.");
|
|
||||||
sah.write(bytes, {at: HEADER_OFFSET_DATA});
|
|
||||||
SAHPool.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB);
|
|
||||||
};
|
|
||||||
|
|
||||||
PoolUtil.wipeFiles = async ()=>SAHPool.reset(true);
|
|
||||||
PoolUtil.unlink = (filename)=>SAHPool.deletePath(filename);
|
|
||||||
|
|
||||||
PoolUtil.removeVfs = async function(){
|
|
||||||
if(!opfsVfs.pointer) return false;
|
|
||||||
capi.sqlite3_vfs_unregister(opfsVfs.pointer);
|
|
||||||
opfsVfs.dispose();
|
|
||||||
try{
|
|
||||||
SAHPool.releaseAccessHandles();
|
|
||||||
if(SAHPool.parentDirHandle){
|
|
||||||
await SAHPool.parentDirHandle.removeEntry(
|
|
||||||
SAHPool.dirHandle.name, {recursive: true}
|
|
||||||
);
|
|
||||||
SAHPool.dirHandle = SAHPool.parentDirHandle = undefined;
|
|
||||||
}
|
|
||||||
}catch(e){
|
|
||||||
warn("removeVfs() failed:",e);
|
|
||||||
/*but otherwise ignored*/
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
return isPromiseReady = SAHPool.reset(!!options.clearOnInit).then(async ()=>{
|
|
||||||
if(SAHPool.$error){
|
if(SAHPool.$error){
|
||||||
throw SAHPool.$error;
|
throw SAHPool.$error;
|
||||||
}
|
}
|
||||||
if(0===SAHPool.getCapacity()){
|
if(0===SAHPool.getCapacity()){
|
||||||
await SAHPool.addCapacity(DEFAULT_CAPACITY);
|
await SAHPool.addCapacity(options.initialCapacity || 6);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
Impls for the sqlite3_io_methods methods. Maintenance reminder:
|
||||||
|
members are in alphabetical order to simplify finding them.
|
||||||
|
*/
|
||||||
|
const ioMethods = {
|
||||||
|
xCheckReservedLock: function(pFile,pOut){
|
||||||
|
log('xCheckReservedLock');
|
||||||
|
SAHPool.storeErr();
|
||||||
|
wasm.poke32(pOut, 1);
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
xClose: function(pFile){
|
||||||
|
SAHPool.storeErr();
|
||||||
|
const file = SAHPool.mapIdToFile.get(pFile);
|
||||||
|
if(file) {
|
||||||
|
try{
|
||||||
|
log(`xClose ${file.path}`);
|
||||||
|
if(file.sq3File) file.sq3File.dispose();
|
||||||
|
file.sah.flush();
|
||||||
|
SAHPool.mapIdToFile.delete(pFile);
|
||||||
|
if(file.flags & capi.SQLITE_OPEN_DELETEONCLOSE){
|
||||||
|
SAHPool.deletePath(file.path);
|
||||||
|
}
|
||||||
|
}catch(e){
|
||||||
|
SAHPool.storeErr(e);
|
||||||
|
return capi.SQLITE_IOERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
xDeviceCharacteristics: function(pFile){
|
||||||
|
return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
|
||||||
|
},
|
||||||
|
xFileControl: function(pFile, opId, pArg){
|
||||||
|
return capi.SQLITE_NOTFOUND;
|
||||||
|
},
|
||||||
|
xFileSize: function(pFile,pSz64){
|
||||||
|
log(`xFileSize`);
|
||||||
|
const file = SAHPool.mapIdToFile.get(pFile);
|
||||||
|
const size = file.sah.getSize() - HEADER_OFFSET_DATA;
|
||||||
|
//log(`xFileSize ${file.path} ${size}`);
|
||||||
|
wasm.poke64(pSz64, BigInt(size));
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
xLock: function(pFile,lockType){
|
||||||
|
log(`xLock ${lockType}`);
|
||||||
|
SAHPool.storeErr();
|
||||||
|
const file = SAHPool.mapIdToFile.get(pFile);
|
||||||
|
file.lockType = lockType;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
xRead: function(pFile,pDest,n,offset64){
|
||||||
|
log(`xRead ${n}@${offset64}`);
|
||||||
|
SAHPool.storeErr();
|
||||||
|
const file = SAHPool.mapIdToFile.get(pFile);
|
||||||
|
log(`xRead ${file.path} ${n} ${offset64}`);
|
||||||
|
try {
|
||||||
|
const nRead = file.sah.read(
|
||||||
|
wasm.heap8u().subarray(pDest, pDest+n),
|
||||||
|
{at: HEADER_OFFSET_DATA + Number(offset64)}
|
||||||
|
);
|
||||||
|
if(nRead < n){
|
||||||
|
wasm.heap8u().fill(0, pDest + nRead, pDest + n);
|
||||||
|
return capi.SQLITE_IOERR_SHORT_READ;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
SAHPool.storeErr(e);
|
||||||
|
return capi.SQLITE_IOERR;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xSectorSize: function(pFile){
|
||||||
|
return SECTOR_SIZE;
|
||||||
|
},
|
||||||
|
xSync: function(pFile,flags){
|
||||||
|
log(`xSync ${flags}`);
|
||||||
|
SAHPool.storeErr();
|
||||||
|
const file = SAHPool.mapIdToFile.get(pFile);
|
||||||
|
//log(`xSync ${file.path} ${flags}`);
|
||||||
|
try{
|
||||||
|
file.sah.flush();
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
SAHPool.storeErr(e);
|
||||||
|
return capi.SQLITE_IOERR;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xTruncate: function(pFile,sz64){
|
||||||
|
log(`xTruncate ${sz64}`);
|
||||||
|
SAHPool.storeErr();
|
||||||
|
const file = SAHPool.mapIdToFile.get(pFile);
|
||||||
|
//log(`xTruncate ${file.path} ${iSize}`);
|
||||||
|
try{
|
||||||
|
file.sah.truncate(HEADER_OFFSET_DATA + Number(sz64));
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
SAHPool.storeErr(e);
|
||||||
|
return capi.SQLITE_IOERR;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xUnlock: function(pFile,lockType){
|
||||||
|
log('xUnlock');
|
||||||
|
const file = SAHPool.mapIdToFile.get(pFile);
|
||||||
|
file.lockType = lockType;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
xWrite: function(pFile,pSrc,n,offset64){
|
||||||
|
SAHPool.storeErr();
|
||||||
|
const file = SAHPool.mapIdToFile.get(pFile);
|
||||||
|
log(`xWrite ${file.path} ${n} ${offset64}`);
|
||||||
|
try{
|
||||||
|
const nBytes = file.sah.write(
|
||||||
|
wasm.heap8u().subarray(pSrc, pSrc+n),
|
||||||
|
{ at: HEADER_OFFSET_DATA + Number(offset64) }
|
||||||
|
);
|
||||||
|
return nBytes === n ? 0 : capi.SQLITE_IOERR;
|
||||||
|
}catch(e){
|
||||||
|
SAHPool.storeErr(e);
|
||||||
|
return capi.SQLITE_IOERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}/*ioMethods*/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Impls for the sqlite3_vfs methods. Maintenance reminder: members
|
||||||
|
are in alphabetical order to simplify finding them.
|
||||||
|
*/
|
||||||
|
const vfsMethods = {
|
||||||
|
xAccess: function(pVfs,zName,flags,pOut){
|
||||||
|
log(`xAccess ${wasm.cstrToJs(zName)}`);
|
||||||
|
SAHPool.storeErr();
|
||||||
|
try{
|
||||||
|
const name = this.getPath(zName);
|
||||||
|
wasm.poke32(pOut, SAHPool.mapFilenameToSAH.has(name) ? 1 : 0);
|
||||||
|
}catch(e){
|
||||||
|
/*ignored*/;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
xCurrentTime: function(pVfs,pOut){
|
||||||
|
wasm.poke(pOut, 2440587.5 + (new Date().getTime()/86400000),
|
||||||
|
'double');
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
xCurrentTimeInt64: function(pVfs,pOut){
|
||||||
|
wasm.poke(pOut, (2440587.5 * 86400000) + new Date().getTime(),
|
||||||
|
'i64');
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
xDelete: function(pVfs, zName, doSyncDir){
|
||||||
|
log(`xDelete ${wasm.cstrToJs(zName)}`);
|
||||||
|
SAHPool.storeErr();
|
||||||
|
try{
|
||||||
|
SAHPool.deletePath(SAHPool.getPath(zName));
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
SAHPool.storeErr(e);
|
||||||
|
return capi.SQLITE_IOERR_DELETE;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xFullPathname: function(pVfs,zName,nOut,pOut){
|
||||||
|
log(`xFullPathname ${wasm.cstrToJs(zName)}`);
|
||||||
|
const i = wasm.cstrncpy(pOut, zName, nOut);
|
||||||
|
return i<nOut ? 0 : capi.SQLITE_CANTOPEN;
|
||||||
|
},
|
||||||
|
xGetLastError: function(pVfs,nOut,pOut){
|
||||||
|
log(`xGetLastError ${nOut}`);
|
||||||
|
const e = SAHPool.popErr();
|
||||||
|
if(e){
|
||||||
|
const scope = wasm.scopedAllocPush();
|
||||||
|
try{
|
||||||
|
const [cMsg, n] = wasm.scopedAllocCString(e.message, true);
|
||||||
|
wasm.cstrncpy(pOut, cMsg, nOut);
|
||||||
|
if(n > nOut) wasm.poke8(pOut + nOut - 1, 0);
|
||||||
|
}catch(e){
|
||||||
|
return capi.SQLITE_NOMEM;
|
||||||
|
}finally{
|
||||||
|
wasm.scopedAllocPop(scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
//xSleep is optionally defined below
|
||||||
|
xOpen: function f(pVfs, zName, pFile, flags, pOutFlags){
|
||||||
|
log(`xOpen ${wasm.cstrToJs(zName)} ${flags}`);
|
||||||
|
try{
|
||||||
|
// First try to open a path that already exists in the file system.
|
||||||
|
const path = (zName && wasm.peek8(zName))
|
||||||
|
? SAHPool.getPath(zName)
|
||||||
|
: getRandomName();
|
||||||
|
let sah = SAHPool.mapFilenameToSAH.get(path);
|
||||||
|
if(!sah && (flags & capi.SQLITE_OPEN_CREATE)) {
|
||||||
|
// File not found so try to create it.
|
||||||
|
if(SAHPool.getFileCount() < SAHPool.getCapacity()) {
|
||||||
|
// Choose an unassociated OPFS file from the pool.
|
||||||
|
sah = SAHPool.nextAvailableSAH();
|
||||||
|
SAHPool.setAssociatedPath(sah, path, flags);
|
||||||
|
}else{
|
||||||
|
// File pool is full.
|
||||||
|
toss('SAH pool is full. Cannot create file',path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!sah){
|
||||||
|
toss('file not found:',path);
|
||||||
|
}
|
||||||
|
// Subsequent methods are only passed the file pointer, so
|
||||||
|
// map the relevant info we need to that pointer.
|
||||||
|
const file = {path, flags, sah};
|
||||||
|
SAHPool.mapIdToFile.set(pFile, file);
|
||||||
|
wasm.poke32(pOutFlags, flags);
|
||||||
|
file.sq3File = new capi.sqlite3_file(pFile);
|
||||||
|
file.sq3File.$pMethods = opfsIoMethods.pointer;
|
||||||
|
file.lockType = capi.SQLITE_LOCK_NONE;
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
SAHPool.storeErr(e);
|
||||||
|
return capi.SQLITE_CANTOPEN;
|
||||||
|
}
|
||||||
|
}/*xOpen()*/
|
||||||
|
}/*vfsMethods*/;
|
||||||
|
|
||||||
|
if(dVfs){
|
||||||
|
/* Inherit certain VFS members from the default VFS,
|
||||||
|
if available. */
|
||||||
|
opfsVfs.$xRandomness = dVfs.$xRandomness;
|
||||||
|
opfsVfs.$xSleep = dVfs.$xSleep;
|
||||||
|
}
|
||||||
|
if(!opfsVfs.$xRandomness){
|
||||||
|
/* If the default VFS has no xRandomness(), add a basic JS impl... */
|
||||||
|
vfsMethods.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){
|
||||||
|
vfsMethods.xSleep = (pVfs,ms)=>0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PoolUtil.$SAHPool = SAHPool/* ONLY for testing and debugging */;
|
||||||
|
PoolUtil.addCapacity = async (n)=>SAHPool.addCapacity(n);
|
||||||
|
PoolUtil.reduceCapacity = async (n)=>SAHPool.reduceCapacity(n);
|
||||||
|
PoolUtil.getCapacity = SAHPool.getCapacity.bind(SAHPool);
|
||||||
|
PoolUtil.getActiveFileCount = SAHPool.getFileCount.bind(SAHPool);
|
||||||
|
PoolUtil.reserveMinimumCapacity = async (min)=>{
|
||||||
|
const c = SAHPool.getCapacity();
|
||||||
|
return (c < min) ? SAHPool.addCapacity(min - c) : c;
|
||||||
|
};
|
||||||
|
|
||||||
|
PoolUtil.exportFile = function(name){
|
||||||
|
const sah = SAHPool.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;
|
||||||
|
};
|
||||||
|
|
||||||
|
PoolUtil.importDb = function(name, bytes){
|
||||||
|
const n = bytes.byteLength;
|
||||||
|
if(n<512 || n%512!=0){
|
||||||
|
toss("Byte array size is invalid for an SQLite db.");
|
||||||
|
}
|
||||||
|
const header = "SQLite format 3";
|
||||||
|
for(let i = 0; i < header.length; ++i){
|
||||||
|
if( header.charCodeAt(i) !== bytes[i] ){
|
||||||
|
toss("Input does not contain an SQLite database header.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const sah = SAHPool.mapFilenameToSAH.get(name)
|
||||||
|
|| SAHPool.nextAvailableSAH()
|
||||||
|
|| toss("No available handles to import to.");
|
||||||
|
sah.write(bytes, {at: HEADER_OFFSET_DATA});
|
||||||
|
SAHPool.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB);
|
||||||
|
};
|
||||||
|
|
||||||
|
PoolUtil.wipeFiles = async ()=>SAHPool.reset(true);
|
||||||
|
PoolUtil.unlink = (filename)=>SAHPool.deletePath(filename);
|
||||||
|
|
||||||
|
PoolUtil.removeVfs = async function(){
|
||||||
|
if(!opfsVfs.pointer) return false;
|
||||||
|
capi.sqlite3_vfs_unregister(opfsVfs.pointer);
|
||||||
|
opfsVfs.dispose();
|
||||||
|
try{
|
||||||
|
SAHPool.releaseAccessHandles();
|
||||||
|
if(SAHPool.parentDirHandle){
|
||||||
|
await SAHPool.parentDirHandle.removeEntry(
|
||||||
|
SAHPool.dirHandle.name, {recursive: true}
|
||||||
|
);
|
||||||
|
SAHPool.dirHandle = SAHPool.parentDirHandle = undefined;
|
||||||
|
}
|
||||||
|
}catch(e){
|
||||||
|
warn("removeVfs() failed:",e);
|
||||||
|
/*but otherwise ignored*/
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
//log("vfs list:",capi.sqlite3_js_vfs_list());
|
//log("vfs list:",capi.sqlite3_js_vfs_list());
|
||||||
sqlite3.vfs.installVfs({
|
sqlite3.vfs.installVfs({
|
||||||
io: {struct: opfsIoMethods, methods: ioMethods},
|
io: {struct: opfsIoMethods, methods: ioMethods},
|
||||||
@ -900,15 +923,20 @@ sqlite3.installOpfsSAHPoolVfs = async function(options=Object.create(null)){
|
|||||||
//log("opfsVfs",opfsVfs,"opfsIoMethods",opfsIoMethods);
|
//log("opfsVfs",opfsVfs,"opfsIoMethods",opfsIoMethods);
|
||||||
//log("vfs list:",capi.sqlite3_js_vfs_list());
|
//log("vfs list:",capi.sqlite3_js_vfs_list());
|
||||||
if(sqlite3.oo1){
|
if(sqlite3.oo1){
|
||||||
|
const oo1 = sqlite3.oo1;
|
||||||
const OpfsSAHPoolDb = function(...args){
|
const OpfsSAHPoolDb = function(...args){
|
||||||
const opt = sqlite3.oo1.DB.dbCtorHelper.normalizeArgs(...args);
|
const opt = oo1.DB.dbCtorHelper.normalizeArgs(...args);
|
||||||
opt.vfs = opfsVfs.$zName;
|
opt.vfs = opfsVfs.$zName;
|
||||||
sqlite3.oo1.DB.dbCtorHelper.call(this, opt);
|
oo1.DB.dbCtorHelper.call(this, opt);
|
||||||
};
|
};
|
||||||
OpfsSAHPoolDb.prototype = Object.create(sqlite3.oo1.DB.prototype);
|
OpfsSAHPoolDb.prototype = Object.create(oo1.DB.prototype);
|
||||||
OpfsSAHPoolDb.PoolUtil = PoolUtil;
|
OpfsSAHPoolDb.PoolUtil = PoolUtil;
|
||||||
sqlite3.oo1.OpfsSAHPoolDb = OpfsSAHPoolDb;
|
if(!oo1.OpfsSAHPoolDb){
|
||||||
sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenSql(
|
oo1.OpfsSAHPoolDb = Object.create(null);
|
||||||
|
oo1.OpfsSAHPoolDb.default = OpfsSAHPoolDb;
|
||||||
|
}
|
||||||
|
oo1.OpfsSAHPoolDb[vfsName] = OpfsSAHPoolDb;
|
||||||
|
oo1.DB.dbCtorHelper.setVfsPostOpenSql(
|
||||||
opfsVfs.pointer,
|
opfsVfs.pointer,
|
||||||
function(oo1Db, sqlite3){
|
function(oo1Db, sqlite3){
|
||||||
sqlite3.capi.sqlite3_exec(oo1Db, [
|
sqlite3.capi.sqlite3_exec(oo1Db, [
|
||||||
@ -920,7 +948,7 @@ sqlite3.installOpfsSAHPoolVfs = async function(options=Object.create(null)){
|
|||||||
);
|
);
|
||||||
}/*extend sqlite3.oo1*/
|
}/*extend sqlite3.oo1*/
|
||||||
log("VFS initialized.");
|
log("VFS initialized.");
|
||||||
return promiseResolve();
|
return PoolUtil;
|
||||||
}).catch(promiseReject);
|
}).catch(promiseReject);
|
||||||
}/*installOpfsSAHPoolVfs()*/;
|
}/*installOpfsSAHPoolVfs()*/;
|
||||||
}/*sqlite3ApiBootstrap.initializers*/);
|
}/*sqlite3ApiBootstrap.initializers*/);
|
||||||
|
@ -56,18 +56,26 @@
|
|||||||
"speedtest1.wasm", ...cliFlagsArray, dbFile
|
"speedtest1.wasm", ...cliFlagsArray, dbFile
|
||||||
];
|
];
|
||||||
App.logBuffer.length = 0;
|
App.logBuffer.length = 0;
|
||||||
|
const ndxSahPool = argv.indexOf('opfs-sahpool');
|
||||||
|
const realSahName = 'opfs-sahpool-speedtest1';
|
||||||
|
if(ndxSahPool>0){
|
||||||
|
argv[ndxSahPool] = realSahName;
|
||||||
|
log("Updated argv for opfs-sahpool: --vfs",realSahName);
|
||||||
|
}
|
||||||
mPost('run-start', [...argv]);
|
mPost('run-start', [...argv]);
|
||||||
if(App.sqlite3.installOpfsSAHPoolVfs
|
if(App.sqlite3.installOpfsSAHPoolVfs
|
||||||
&& !App.sqlite3.$SAHPoolUtil
|
&& !App.sqlite3.$SAHPoolUtil
|
||||||
&& cliFlagsArray.indexOf('opfs-sahpool')>=0){
|
&& ndxSahPool>0){
|
||||||
log("Installing opfs-sahpool...");
|
log("Installing opfs-sahpool as",realSahName,"...");
|
||||||
await App.sqlite3.installOpfsSAHPoolVfs({
|
await App.sqlite3.installOpfsSAHPoolVfs({
|
||||||
directory: '.speedtest1-sahpool',
|
name: realSahName,
|
||||||
initialCapacity: 3,
|
initialCapacity: 3,
|
||||||
clearOnInit: true
|
clearOnInit: true,
|
||||||
|
verbosity: 2
|
||||||
}).then(PoolUtil=>{
|
}).then(PoolUtil=>{
|
||||||
log("opfs-sahpool successfully installed.");
|
log("opfs-sahpool successfully installed as",realSahName);
|
||||||
App.sqlite3.$SAHPoolUtil = PoolUtil;
|
App.sqlite3.$SAHPoolUtil = PoolUtil;
|
||||||
|
//console.log("sqlite3.oo1.OpfsSAHPoolDb =", App.sqlite3.oo1.OpfsSAHPoolDb);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
App.wasm.xCall('wasm_main', argv.length,
|
App.wasm.xCall('wasm_main', argv.length,
|
||||||
|
14
manifest
14
manifest
@ -1,5 +1,5 @@
|
|||||||
C Another\s-DSQLITE_TEMP_STORE=3\sto\s2\schange\sfor\sthe\swasm\sbuild.
|
C Refactor\sopfs-sahpool\sto\ssupport\smultiple\sinstances,\seach\swith\sa\sseparate\sVFS\sname\sand\sdirectory.
|
||||||
D 2023-07-17T07:43:04.661
|
D 2023-07-18T12:09:16.750
|
||||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||||
@ -502,7 +502,7 @@ F ext/wasm/api/sqlite3-api-worker1.js 9f32af64df1a031071912eea7a201557fe39b17386
|
|||||||
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
|
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
|
||||||
F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256dfb4f96555b865dbb7a6b65e379
|
F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256dfb4f96555b865dbb7a6b65e379
|
||||||
F ext/wasm/api/sqlite3-v-helper.js e5c202a9ecde9ef818536d3f5faf26c03a1a9f5192b1ddea8bdabf30d75ef487
|
F ext/wasm/api/sqlite3-v-helper.js e5c202a9ecde9ef818536d3f5faf26c03a1a9f5192b1ddea8bdabf30d75ef487
|
||||||
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.js ff3495c0140787fc0232081582a6126f22b7b2e7894bee83061e8bf9eea2ef7f
|
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.js a3307deb47d7d7a9a6e202a20b19252fa12fbeb60aeee11008ee0358a7137286
|
||||||
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 842d55b35a871ee5483cc5e0cf067a968362b4d61321f08c71aab5505c72f556
|
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 842d55b35a871ee5483cc5e0cf067a968362b4d61321f08c71aab5505c72f556
|
||||||
F ext/wasm/api/sqlite3-wasm.c 8867f1d41c112fb4a2cfe22ff224eccaf309fcdea266cee0ec554f85db72ef0f
|
F ext/wasm/api/sqlite3-wasm.c 8867f1d41c112fb4a2cfe22ff224eccaf309fcdea266cee0ec554f85db72ef0f
|
||||||
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f
|
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f
|
||||||
@ -540,7 +540,7 @@ F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd84223150
|
|||||||
F ext/wasm/speedtest1-wasmfs.html 0e9d335a9b5b5fafe6e1bc8dc0f0ca7e22e6eb916682a2d7c36218bb7d67379d
|
F ext/wasm/speedtest1-wasmfs.html 0e9d335a9b5b5fafe6e1bc8dc0f0ca7e22e6eb916682a2d7c36218bb7d67379d
|
||||||
F ext/wasm/speedtest1-wasmfs.mjs ac5cadbf4ffe69e9eaac8b45e8523f030521e02bb67d654c6eb5236d9c456cbe
|
F ext/wasm/speedtest1-wasmfs.mjs ac5cadbf4ffe69e9eaac8b45e8523f030521e02bb67d654c6eb5236d9c456cbe
|
||||||
F ext/wasm/speedtest1-worker.html e33e2064bda572c0c3ebaec7306c35aa758d9d27e245d67e807f8cc4a9351cc5
|
F ext/wasm/speedtest1-worker.html e33e2064bda572c0c3ebaec7306c35aa758d9d27e245d67e807f8cc4a9351cc5
|
||||||
F ext/wasm/speedtest1-worker.js 4f18caa941ed89d42af46c598e7f7fe31cecac853e0b370d235adcb19ce8cbee
|
F ext/wasm/speedtest1-worker.js 315d26198c46be7c85e26fda15d80ef882424276abde25ffd8b026fb02a35d8c
|
||||||
F ext/wasm/speedtest1.html ff048b4a623aa192e83e143e48f1ce2a899846dd42c023fdedc8772b6e3f07da
|
F ext/wasm/speedtest1.html ff048b4a623aa192e83e143e48f1ce2a899846dd42c023fdedc8772b6e3f07da
|
||||||
F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
|
F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
|
||||||
F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
|
F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
|
||||||
@ -2044,8 +2044,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
|||||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||||
P 2c9c95d087c7db2a975ffae15af5ade621411c493145b889f0c9157a55c4952a
|
P 5ad8f9987c11b5db67edd69a3471ff388140d6268d5d3f5a05dec6bb6d92ac05
|
||||||
R f9198174ee4df68f95986dfc1d71ac1a
|
R a597bda217a200863fd7fdd91e2ff569
|
||||||
U stephan
|
U stephan
|
||||||
Z 5f625ecf88ca9a37df1d1dfeecd67369
|
Z 61da1feaecfdf3b26fae912b9de308b1
|
||||||
# Remove this line to create a well-formed Fossil manifest.
|
# Remove this line to create a well-formed Fossil manifest.
|
||||||
|
@ -1 +1 @@
|
|||||||
5ad8f9987c11b5db67edd69a3471ff388140d6268d5d3f5a05dec6bb6d92ac05
|
d036eaf6ac60c576428db40f015733c5d5425f7d613194fd8d9d4d98659077c4
|
Reference in New Issue
Block a user