mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-27 20:41:58 +03:00
Merge trunk into the sahpool-digest branch.
FossilOrigin-Name: fc1eeb7d1f2880907b0fe71a8c572dd7cd74a5d65ec0177332976ad2f8c2b216
This commit is contained in:
@ -544,22 +544,10 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
currently-opened client-specified filenames. */
|
||||
getFileNames(){
|
||||
const rc = [];
|
||||
const iter = this.#mapFilenameToSAH.keys();
|
||||
for(const n of iter) rc.push(n);
|
||||
for(const n of this.#mapFilenameToSAH.keys()) rc.push(n);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// #createFileObject(sah,clientName,opaqueName){
|
||||
// const f = Object.assign(Object.create(null),{
|
||||
// clientName, opaqueName
|
||||
// });
|
||||
// this.#mapSAHToMeta.set(sah, f);
|
||||
// return f;
|
||||
// }
|
||||
// #unmapFileObject(sah){
|
||||
// this.#mapSAHToMeta.delete(sah);
|
||||
// }
|
||||
|
||||
/**
|
||||
Adds n files to the pool's capacity. This change is
|
||||
persistent across settings. Returns a Promise which resolves
|
||||
@ -600,8 +588,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
}
|
||||
|
||||
/**
|
||||
Releases all currently-opened SAHs. The only legal
|
||||
operation after this is acquireAccessHandles().
|
||||
Releases all currently-opened SAHs. The only legal operation
|
||||
after this is acquireAccessHandles() or (if this is called from
|
||||
pauseVfs()) either of isPaused() or unpauseVfs().
|
||||
*/
|
||||
releaseAccessHandles(){
|
||||
for(const ah of this.#mapSAHToName.keys()) ah.close();
|
||||
@ -611,17 +600,21 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
}
|
||||
|
||||
/**
|
||||
Opens all files under this.vfsDir/this.#dhOpaque and acquires
|
||||
a SAH for each. returns a Promise which resolves to no value
|
||||
but completes once all SAHs are acquired. If acquiring an SAH
|
||||
throws, SAHPool.$error will contain the corresponding
|
||||
exception.
|
||||
Opens all files under this.vfsDir/this.#dhOpaque and acquires a
|
||||
SAH for each. Returns a Promise which resolves to no value but
|
||||
completes once all SAHs are acquired. If acquiring an SAH
|
||||
throws, this.$error will contain the corresponding Error
|
||||
object.
|
||||
|
||||
If it throws, it releases any SAHs which it may have
|
||||
acquired before the exception was thrown, leaving the VFS in a
|
||||
well-defined but unusable state.
|
||||
|
||||
If clearFiles is true, the client-stored state of each file is
|
||||
cleared when its handle is acquired, including its name, flags,
|
||||
and any data stored after the metadata block.
|
||||
*/
|
||||
async acquireAccessHandles(clearFiles){
|
||||
async acquireAccessHandles(clearFiles=false){
|
||||
const files = [];
|
||||
for await (const [name,h] of this.#dhOpaque){
|
||||
if('file'===h.kind){
|
||||
@ -890,12 +883,18 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
Removes this object's sqlite3_vfs registration and shuts down
|
||||
this object, releasing all handles, mappings, and whatnot,
|
||||
including deleting its data directory. There is currently no
|
||||
way to "revive" the object and reaquire its resources.
|
||||
way to "revive" the object and reaquire its
|
||||
resources. Similarly, there is no recovery strategy if removal
|
||||
of any given SAH fails, so such errors are ignored by this
|
||||
function.
|
||||
|
||||
This function is intended primarily for testing.
|
||||
|
||||
Resolves to true if it did its job, false if the
|
||||
VFS has already been shut down.
|
||||
|
||||
@see pauseVfs()
|
||||
@see unpauseVfs()
|
||||
*/
|
||||
async removeVfs(){
|
||||
if(!this.#cVfs.pointer || !this.#dhOpaque) return false;
|
||||
@ -911,13 +910,77 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
);
|
||||
this.#dhVfsRoot = this.#dhVfsParent = undefined;
|
||||
}catch(e){
|
||||
sqlite3.config.error(this.vfsName,"removeVfs() failed:",e);
|
||||
sqlite3.config.error(this.vfsName,"removeVfs() failed with no recovery strategy:",e);
|
||||
/*otherwise ignored - there is no recovery strategy*/
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
"Pauses" this VFS by unregistering it from SQLite and
|
||||
relinquishing all open SAHs, leaving the associated files
|
||||
intact. If this object is already paused, this is a
|
||||
no-op. Returns this object.
|
||||
|
||||
This function throws if SQLite has any opened file handles
|
||||
hosted by this VFS, as the alternative would be to invoke
|
||||
Undefined Behavior by closing file handles out from under the
|
||||
library. Similarly, automatically closing any database handles
|
||||
opened by this VFS would invoke Undefined Behavior in
|
||||
downstream code which is holding those pointers.
|
||||
|
||||
If this function throws due to open file handles then it has
|
||||
no side effects. If the OPFS API throws while closing handles
|
||||
then the VFS is left in an undefined state.
|
||||
|
||||
@see isPaused()
|
||||
@see unpauseVfs()
|
||||
*/
|
||||
pauseVfs(){
|
||||
if(this.#mapS3FileToOFile_.size>0){
|
||||
sqlite3.SQLite3Error.toss(
|
||||
capi.SQLITE_MISUSE, "Cannot pause VFS",
|
||||
this.vfsName,"because it has opened files."
|
||||
);
|
||||
}
|
||||
if(this.#mapSAHToName.size>0){
|
||||
capi.sqlite3_vfs_unregister(this.vfsName);
|
||||
this.releaseAccessHandles();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns true if this pool is currently paused else false.
|
||||
|
||||
@see pauseVfs()
|
||||
@see unpauseVfs()
|
||||
*/
|
||||
isPaused(){
|
||||
return 0===this.#mapSAHToName.size;
|
||||
}
|
||||
|
||||
/**
|
||||
"Unpauses" this VFS, reacquiring all SAH's and (if successful)
|
||||
re-registering it with SQLite. This is a no-op if the VFS is
|
||||
not currently paused.
|
||||
|
||||
The returned Promise resolves to this object. See
|
||||
acquireAccessHandles() for how it behaves if it throws due to
|
||||
SAH acquisition failure.
|
||||
|
||||
@see isPaused()
|
||||
@see pauseVfs()
|
||||
*/
|
||||
async unpauseVfs(){
|
||||
if(0===this.#mapSAHToName.size){
|
||||
return this.acquireAccessHandles(false).
|
||||
then(()=>capi.sqlite3_vfs_register(this.#cVfs, 0),this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
//! Documented elsewhere in this file.
|
||||
exportFile(name){
|
||||
const sah = this.#mapFilenameToSAH.get(name) || toss("File not found:",name);
|
||||
@ -1042,6 +1105,10 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
async removeVfs(){ return this.#p.removeVfs() }
|
||||
|
||||
pauseVfs(){ this.#p.pauseVfs(); return this; }
|
||||
async unpauseVfs(){ return this.#p.unpauseVfs().then(()=>this); }
|
||||
isPaused(){ return this.#p.isPaused() }
|
||||
|
||||
}/* class OpfsSAHPoolUtil */;
|
||||
|
||||
/**
|
||||
@ -1275,6 +1342,41 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
Clears all client-defined state of all SAHs and makes all of them
|
||||
available for re-use by the pool. Results are undefined if any such
|
||||
handles are currently in use, e.g. by an sqlite3 db.
|
||||
|
||||
APIs specific to the "pause" capability (added in version 3.49):
|
||||
|
||||
Summary: "pausing" the VFS disassociates it from SQLite and
|
||||
relinquishes its SAHs so that they may be opened by another
|
||||
instance of this VFS (running in a separate tab/page or Worker).
|
||||
"Unpausing" it takes back control, if able.
|
||||
|
||||
- pauseVfs()
|
||||
|
||||
"Pauses" this VFS by unregistering it from SQLite and
|
||||
relinquishing all open SAHs, leaving the associated files intact.
|
||||
This enables pages/tabs to coordinate semi-concurrent usage of
|
||||
this VFS. If this object is already paused, this is a
|
||||
no-op. Returns this object. Throws if SQLite has any opened file
|
||||
handles hosted by this VFS. If this function throws due to open
|
||||
file handles then it has no side effects. If the OPFS API throws
|
||||
while closing handles then the VFS is left in an undefined state.
|
||||
|
||||
- isPaused()
|
||||
|
||||
Returns true if this VFS is paused, else false.
|
||||
|
||||
- [async] unpauseVfs()
|
||||
|
||||
Restores the VFS to an active state after having called
|
||||
pauseVfs() on it. This is a no-op if the VFS is not paused. The
|
||||
returned Promise resolves to this object on success. A rejected
|
||||
Promise means there was a problem reacquiring the SAH handles
|
||||
(possibly because they're in use by another instance or have
|
||||
since been removed). Generically speaking, there is no recovery
|
||||
strategy for that type of error, but if the problem is simply
|
||||
that the OPFS files are locked, then a later attempt to unpause
|
||||
it, made after the concurrent instance releases the SAHs, may
|
||||
recover from the situation.
|
||||
*/
|
||||
sqlite3.installOpfsSAHPoolVfs = async function(options=Object.create(null)){
|
||||
options = Object.assign(Object.create(null), optionDefaults, (options||{}));
|
||||
|
Reference in New Issue
Block a user