mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
Internal restructuring of the OPFS sqlite3_vfs in order to facilitate certain experimentation and improve error reporting/hints if it cannot be activated. Deprecate the name sqlite3.opfs.OpfsDb, preferring sqlite3.oo1.OpfsDb for consistency with JsStorageDb and any future DB subclasses.
FossilOrigin-Name: 0c5c51f4fb04a4b90c50ec9704cfea9a3fb7d7d0ee55c1b0d4476129188217a6
This commit is contained in:
@ -76,15 +76,25 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
`opfs` property, containing several OPFS-specific utilities.
|
||||
*/
|
||||
const installOpfsVfs = function callee(options){
|
||||
if(!self.SharedArrayBuffer ||
|
||||
!self.Atomics ||
|
||||
!self.FileSystemHandle ||
|
||||
if(!self.SharedArrayBuffer
|
||||
|| !self.Atomics){
|
||||
return Promise.reject(
|
||||
new Error("Cannot install OPFS: Missing SharedArrayBuffer and/or Atomics. "+
|
||||
"The server must emit the COOP/COEP response headers to enable those. "+
|
||||
"See https://sqlite.org/wasm/doc/trunk/persistence.md#coop-coep")
|
||||
);
|
||||
}else if(self.window===self && self.document){
|
||||
return Promise.reject(
|
||||
new Error("The OPFS sqlite3_vfs cannot run in the main thread "+
|
||||
"because it requires Atomics.wait().")
|
||||
);
|
||||
}else if(!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.")
|
||||
new Error("Missing required OPFS APIs.")
|
||||
);
|
||||
}
|
||||
if(!options || 'object'!==typeof options){
|
||||
@ -134,6 +144,18 @@ const installOpfsVfs = function callee(options){
|
||||
OPFS-specific sqlite3_vfs evolves.
|
||||
*/
|
||||
const opfsUtil = Object.create(null);
|
||||
|
||||
/**
|
||||
Returns true if _this_ thread has access to the OPFS APIs.
|
||||
*/
|
||||
const thisThreadHasOPFS = ()=>{
|
||||
return self.FileSystemHandle &&
|
||||
self.FileSystemDirectoryHandle &&
|
||||
self.FileSystemFileHandle &&
|
||||
self.FileSystemFileHandle.prototype.createSyncAccessHandle &&
|
||||
navigator.storage.getDirectory;
|
||||
};
|
||||
|
||||
/**
|
||||
Not part of the public API. Solely for internal/development
|
||||
use.
|
||||
@ -1179,12 +1201,16 @@ const installOpfsVfs = function callee(options){
|
||||
//consideration.
|
||||
|
||||
if(sqlite3.oo1){
|
||||
opfsUtil.OpfsDb = function(...args){
|
||||
const OpfsDb = function(...args){
|
||||
const opt = sqlite3.oo1.DB.dbCtorHelper.normalizeArgs(...args);
|
||||
opt.vfs = opfsVfs.$zName;
|
||||
sqlite3.oo1.DB.dbCtorHelper.call(this, opt);
|
||||
};
|
||||
opfsUtil.OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype);
|
||||
sqlite3.oo1.OpfsDb =
|
||||
opfsUtil.OpfsDb /* sqlite3.opfs.OpfsDb => deprecated name -
|
||||
will be phased out Real Soon */ =
|
||||
OpfsDb;
|
||||
OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype);
|
||||
sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenSql(
|
||||
opfsVfs.pointer,
|
||||
[
|
||||
@ -1206,13 +1232,6 @@ const installOpfsVfs = function callee(options){
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
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 = function(){
|
||||
const scope = wasm.scopedAllocPush();
|
||||
const sq3File = new sqlite3_file();
|
||||
@ -1282,6 +1301,11 @@ const installOpfsVfs = function callee(options){
|
||||
W.onmessage = function({data}){
|
||||
//log("Worker.onmessage:",data);
|
||||
switch(data.type){
|
||||
case 'opfs-unavailable':
|
||||
/* Async proxy has determined that OPFS is unavailable. There's
|
||||
nothing more for us to do here. */
|
||||
promiseReject(new Error(data.payload.join(' ')));
|
||||
break;
|
||||
case 'opfs-async-loaded':
|
||||
/*Arrives as soon as the asyc proxy finishes loading.
|
||||
Pass our config and shared state on to the async worker.*/
|
||||
@ -1308,6 +1332,7 @@ const installOpfsVfs = function callee(options){
|
||||
warn("Running sanity checks because of opfs-sanity-check URL arg...");
|
||||
sanityCheck();
|
||||
}
|
||||
if(thisThreadHasOPFS()){
|
||||
navigator.storage.getDirectory().then((d)=>{
|
||||
W.onerror = W._originalOnError;
|
||||
delete W._originalOnError;
|
||||
@ -1316,6 +1341,9 @@ const installOpfsVfs = function callee(options){
|
||||
log("End of OPFS sqlite3_vfs setup.", opfsVfs);
|
||||
promiseResolve(sqlite3);
|
||||
});
|
||||
}else{
|
||||
promiseResolve(sqlite3);
|
||||
}
|
||||
}catch(e){
|
||||
error(e);
|
||||
promiseReject(e);
|
||||
@ -1334,9 +1362,6 @@ const installOpfsVfs = function callee(options){
|
||||
installOpfsVfs.defaultProxyUri =
|
||||
"sqlite3-opfs-async-proxy.js";
|
||||
self.sqlite3ApiBootstrap.initializersAsync.push(async (sqlite3)=>{
|
||||
if(sqlite3.scriptInfo && !sqlite3.scriptInfo.isWorker){
|
||||
return;
|
||||
}
|
||||
try{
|
||||
let proxyJs = installOpfsVfs.defaultProxyUri;
|
||||
if(sqlite3.scriptInfo.sqlite3Dir){
|
||||
|
@ -47,43 +47,45 @@
|
||||
usage of those methods to remove the "await".
|
||||
*/
|
||||
"use strict";
|
||||
const toss = function(...args){throw new Error(args.join(' '))};
|
||||
if(self.window === self){
|
||||
const wPost = (type,...args)=>postMessage({type, payload:args});
|
||||
const installAsyncProxy = function(self){
|
||||
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){
|
||||
}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);
|
||||
*/
|
||||
const state = Object.create(null);
|
||||
|
||||
/**
|
||||
/**
|
||||
verbose:
|
||||
|
||||
0 = no logging output
|
||||
1 = only errors
|
||||
2 = warnings and errors
|
||||
3 = debug, warnings, and errors
|
||||
*/
|
||||
state.verbose = 1;
|
||||
*/
|
||||
state.verbose = 1;
|
||||
|
||||
const loggers = {
|
||||
const loggers = {
|
||||
0:console.error.bind(console),
|
||||
1:console.warn.bind(console),
|
||||
2:console.log.bind(console)
|
||||
};
|
||||
const logImpl = (level,...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);
|
||||
const metrics = Object.create(null);
|
||||
metrics.reset = ()=>{
|
||||
};
|
||||
const log = (...args)=>logImpl(2, ...args);
|
||||
const warn = (...args)=>logImpl(1, ...args);
|
||||
const error = (...args)=>logImpl(0, ...args);
|
||||
const metrics = Object.create(null);
|
||||
metrics.reset = ()=>{
|
||||
let k;
|
||||
const r = (m)=>(m.count = m.time = m.wait = 0);
|
||||
for(k in state.opIds){
|
||||
@ -94,8 +96,8 @@ metrics.reset = ()=>{
|
||||
s.count = s.time = 0;
|
||||
s = metrics.s11n.deserialize = Object.create(null);
|
||||
s.count = s.time = 0;
|
||||
};
|
||||
metrics.dump = ()=>{
|
||||
};
|
||||
metrics.dump = ()=>{
|
||||
let k, n = 0, t = 0, w = 0;
|
||||
for(k in state.opIds){
|
||||
const m = metrics[k];
|
||||
@ -110,17 +112,17 @@ metrics.dump = ()=>{
|
||||
"\nTotal of",n,"op(s) for",t,"ms",
|
||||
"approx",w,"ms spent waiting on OPFS APIs.");
|
||||
console.log("Serialization metrics:",metrics.s11n);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
__openFiles is a 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);
|
||||
/**
|
||||
*/
|
||||
const __openFiles = Object.create(null);
|
||||
/**
|
||||
__implicitLocks is a Set of sqlite3_file pointers (integers) which were
|
||||
"auto-locked". i.e. those for which we obtained a sync access
|
||||
handle without an explicit xLock() call. Such locks will be
|
||||
@ -132,29 +134,29 @@ const __openFiles = Object.create(null);
|
||||
operation which acquires them, we pay a massive performance
|
||||
penalty: speedtest1 benchmarks take up to 4x as long. By delaying
|
||||
the lock release until idle time, the hit is negligible.
|
||||
*/
|
||||
const __implicitLocks = new Set();
|
||||
*/
|
||||
const __implicitLocks = new Set();
|
||||
|
||||
/**
|
||||
/**
|
||||
Expects an OPFS file path. It gets resolved, such that ".."
|
||||
components are properly expanded, and returned. If the 2nd arg is
|
||||
true, the result is returned as an array of path elements, else an
|
||||
absolute path string is returned.
|
||||
*/
|
||||
const getResolvedPath = function(filename,splitIt){
|
||||
*/
|
||||
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
|
||||
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 getDirForFilename = async function f(absFilename, createDirs = false){
|
||||
*/
|
||||
const getDirForFilename = async function f(absFilename, createDirs = false){
|
||||
const path = getResolvedPath(absFilename, true);
|
||||
const filename = path.pop();
|
||||
let dh = state.rootDir;
|
||||
@ -164,9 +166,9 @@ const getDirForFilename = async function f(absFilename, createDirs = false){
|
||||
}
|
||||
}
|
||||
return [dh, filename];
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
If the given file-holding object has a sync handle attached to it,
|
||||
that handle is remove and asynchronously closed. Though it may
|
||||
sound sensible to continue work as soon as the close() returns
|
||||
@ -175,8 +177,8 @@ const getDirForFilename = async function f(absFilename, createDirs = false){
|
||||
because they may happen out of order from the close(). OPFS does
|
||||
not guaranty that the actual order of operations is retained in
|
||||
such cases. i.e. always "await" on the result of this function.
|
||||
*/
|
||||
const closeSyncHandle = async (fh)=>{
|
||||
*/
|
||||
const closeSyncHandle = async (fh)=>{
|
||||
if(fh.syncHandle){
|
||||
log("Closing sync handle for",fh.filenameAbs);
|
||||
const h = fh.syncHandle;
|
||||
@ -185,9 +187,9 @@ const closeSyncHandle = async (fh)=>{
|
||||
__implicitLocks.delete(fh.fid);
|
||||
return h.close();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
A proxy for closeSyncHandle() which is guaranteed to not throw.
|
||||
|
||||
This function is part of a lock/unlock step in functions which
|
||||
@ -197,16 +199,16 @@ const closeSyncHandle = async (fh)=>{
|
||||
_attempt_ at reducing cross-tab contention but it may prove
|
||||
to be more of a problem than a solution and may need to be
|
||||
removed.
|
||||
*/
|
||||
const closeSyncHandleNoThrow = async (fh)=>{
|
||||
*/
|
||||
const closeSyncHandleNoThrow = async (fh)=>{
|
||||
try{await closeSyncHandle(fh)}
|
||||
catch(e){
|
||||
warn("closeSyncHandleNoThrow() ignoring:",e,fh);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/* Release all auto-locks. */
|
||||
const releaseImplicitLocks = async ()=>{
|
||||
/* Release all auto-locks. */
|
||||
const releaseImplicitLocks = async ()=>{
|
||||
if(__implicitLocks.size){
|
||||
/* Release all auto-locks. */
|
||||
for(const fid of __implicitLocks){
|
||||
@ -215,30 +217,30 @@ const releaseImplicitLocks = async ()=>{
|
||||
log("Auto-unlocked",fid,fh.filenameAbs);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
An experiment in improving concurrency by freeing up implicit locks
|
||||
sooner. This is known to impact performance dramatically but it has
|
||||
also shown to improve concurrency considerably.
|
||||
|
||||
If fh.releaseImplicitLocks is truthy and fh is in __implicitLocks,
|
||||
this routine returns closeSyncHandleNoThrow(), else it is a no-op.
|
||||
*/
|
||||
const releaseImplicitLock = async (fh)=>{
|
||||
*/
|
||||
const releaseImplicitLock = async (fh)=>{
|
||||
if(fh.releaseImplicitLocks && __implicitLocks.has(fh.fid)){
|
||||
return closeSyncHandleNoThrow(fh);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
An error class specifically for use with getSyncHandle(), the goal
|
||||
of which is to eventually be able to distinguish unambiguously
|
||||
between locking-related failures and other types, noting that we
|
||||
cannot currently do so because createSyncAccessHandle() does not
|
||||
define its exceptions in the required level of detail.
|
||||
*/
|
||||
class GetSyncHandleError extends Error {
|
||||
*/
|
||||
class GetSyncHandleError extends Error {
|
||||
constructor(errorObject, ...msg){
|
||||
super();
|
||||
this.error = errorObject;
|
||||
@ -248,8 +250,8 @@ class GetSyncHandleError extends Error {
|
||||
].join(' ');
|
||||
this.name = 'GetSyncHandleError';
|
||||
}
|
||||
};
|
||||
GetSyncHandleError.convertRc = (e,rc)=>{
|
||||
};
|
||||
GetSyncHandleError.convertRc = (e,rc)=>{
|
||||
if(0){
|
||||
/* This approach makes the very wild assumption that such a
|
||||
failure _is_ a locking error. In practice that appears to be
|
||||
@ -264,8 +266,8 @@ GetSyncHandleError.convertRc = (e,rc)=>{
|
||||
}else{
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
/**
|
||||
}
|
||||
/**
|
||||
Returns the sync access handle associated with the given file
|
||||
handle object (which must be a valid handle object, as created by
|
||||
xOpen()), lazily opening it if needed.
|
||||
@ -275,8 +277,8 @@ GetSyncHandleError.convertRc = (e,rc)=>{
|
||||
will wait briefly and try again, up to 3 times. If acquisition
|
||||
still fails at that point it will give up and propagate the
|
||||
exception.
|
||||
*/
|
||||
const getSyncHandle = async (fh,opName)=>{
|
||||
*/
|
||||
const getSyncHandle = async (fh,opName)=>{
|
||||
if(!fh.syncHandle){
|
||||
const t = performance.now();
|
||||
log("Acquiring sync handle for",fh.filenameAbs);
|
||||
@ -310,25 +312,25 @@ const getSyncHandle = async (fh,opName)=>{
|
||||
}
|
||||
}
|
||||
return fh.syncHandle;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
Stores the given value at state.sabOPView[state.opIds.rc] and then
|
||||
Atomics.notify()'s it.
|
||||
*/
|
||||
const storeAndNotify = (opName, value)=>{
|
||||
*/
|
||||
const storeAndNotify = (opName, value)=>{
|
||||
log(opName+"() => notify(",value,")");
|
||||
Atomics.store(state.sabOPView, state.opIds.rc, value);
|
||||
Atomics.notify(state.sabOPView, state.opIds.rc);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
Throws if fh is a file-holding object which is flagged as read-only.
|
||||
*/
|
||||
const affirmNotRO = function(opName,fh){
|
||||
*/
|
||||
const affirmNotRO = function(opName,fh){
|
||||
if(fh.readOnly) toss(opName+"(): File is read-only: "+fh.filenameAbs);
|
||||
};
|
||||
const affirmLocked = function(opName,fh){
|
||||
};
|
||||
const affirmLocked = function(opName,fh){
|
||||
//if(!fh.syncHandle) toss(opName+"(): File does not have a lock: "+fh.filenameAbs);
|
||||
/**
|
||||
Currently a no-op, as speedtest1 triggers xRead() without a
|
||||
@ -337,54 +339,54 @@ const affirmLocked = function(opName,fh){
|
||||
acquisition of a lock but never let it go until xUnlock() is
|
||||
called (which it likely won't be if xLock() was not called).
|
||||
*/
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
We track 2 different timers: the "metrics" timer records how much
|
||||
time we spend performing work. The "wait" timer records how much
|
||||
time we spend waiting on the underlying OPFS timer. See the calls
|
||||
to mTimeStart(), mTimeEnd(), wTimeStart(), and wTimeEnd()
|
||||
throughout this file to see how they're used.
|
||||
*/
|
||||
const __mTimer = Object.create(null);
|
||||
__mTimer.op = undefined;
|
||||
__mTimer.start = undefined;
|
||||
const mTimeStart = (op)=>{
|
||||
*/
|
||||
const __mTimer = Object.create(null);
|
||||
__mTimer.op = undefined;
|
||||
__mTimer.start = undefined;
|
||||
const mTimeStart = (op)=>{
|
||||
__mTimer.start = performance.now();
|
||||
__mTimer.op = op;
|
||||
//metrics[op] || toss("Maintenance required: missing metrics for",op);
|
||||
++metrics[op].count;
|
||||
};
|
||||
const mTimeEnd = ()=>(
|
||||
};
|
||||
const mTimeEnd = ()=>(
|
||||
metrics[__mTimer.op].time += performance.now() - __mTimer.start
|
||||
);
|
||||
const __wTimer = Object.create(null);
|
||||
__wTimer.op = undefined;
|
||||
__wTimer.start = undefined;
|
||||
const wTimeStart = (op)=>{
|
||||
);
|
||||
const __wTimer = Object.create(null);
|
||||
__wTimer.op = undefined;
|
||||
__wTimer.start = undefined;
|
||||
const wTimeStart = (op)=>{
|
||||
__wTimer.start = performance.now();
|
||||
__wTimer.op = op;
|
||||
//metrics[op] || toss("Maintenance required: missing metrics for",op);
|
||||
};
|
||||
const wTimeEnd = ()=>(
|
||||
};
|
||||
const wTimeEnd = ()=>(
|
||||
metrics[__wTimer.op].wait += performance.now() - __wTimer.start
|
||||
);
|
||||
);
|
||||
|
||||
/**
|
||||
/**
|
||||
Gets set to true by the 'opfs-async-shutdown' command to quit the
|
||||
wait loop. This is only intended for debugging purposes: we cannot
|
||||
inspect this file's state while the tight waitLoop() is running and
|
||||
need a way to stop that loop for introspection purposes.
|
||||
*/
|
||||
let flagAsyncShutdown = false;
|
||||
*/
|
||||
let flagAsyncShutdown = false;
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
Asynchronous wrappers for sqlite3_vfs and sqlite3_io_methods
|
||||
methods, as well as helpers like mkdir(). Maintenance reminder:
|
||||
members are in alphabetical order to simplify finding them.
|
||||
*/
|
||||
const vfsAsyncImpls = {
|
||||
*/
|
||||
const vfsAsyncImpls = {
|
||||
'opfs-async-metrics': async ()=>{
|
||||
mTimeStart('opfs-async-metrics');
|
||||
metrics.dump();
|
||||
@ -700,9 +702,9 @@ const vfsAsyncImpls = {
|
||||
storeAndNotify('xWrite',rc);
|
||||
mTimeEnd();
|
||||
}
|
||||
}/*vfsAsyncImpls*/;
|
||||
}/*vfsAsyncImpls*/;
|
||||
|
||||
const initS11n = ()=>{
|
||||
const initS11n = ()=>{
|
||||
/**
|
||||
ACHTUNG: this code is 100% duplicated in the other half of this
|
||||
proxy! The documentation is maintained in the "synchronous half".
|
||||
@ -806,9 +808,9 @@ const initS11n = ()=>{
|
||||
: ()=>{};
|
||||
|
||||
return state.s11n;
|
||||
}/*initS11n()*/;
|
||||
}/*initS11n()*/;
|
||||
|
||||
const waitLoop = async function f(){
|
||||
const waitLoop = async function f(){
|
||||
const opHandlers = Object.create(null);
|
||||
for(let k of Object.keys(state.opIds)){
|
||||
const vi = vfsAsyncImpls[k];
|
||||
@ -848,10 +850,9 @@ const waitLoop = async function f(){
|
||||
error('in waitLoop():',e);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
navigator.storage.getDirectory().then(function(d){
|
||||
const wMsg = (type)=>postMessage({type});
|
||||
navigator.storage.getDirectory().then(function(d){
|
||||
state.rootDir = d;
|
||||
self.onmessage = function({data}){
|
||||
switch(data.type){
|
||||
@ -880,7 +881,7 @@ navigator.storage.getDirectory().then(function(d){
|
||||
initS11n();
|
||||
metrics.reset();
|
||||
log("init state",state);
|
||||
wMsg('opfs-async-inited');
|
||||
wPost('opfs-async-inited');
|
||||
waitLoop();
|
||||
break;
|
||||
}
|
||||
@ -896,5 +897,21 @@ navigator.storage.getDirectory().then(function(d){
|
||||
break;
|
||||
}
|
||||
};
|
||||
wMsg('opfs-async-loaded');
|
||||
}).catch((e)=>error("error initializing OPFS asyncer:",e));
|
||||
wPost('opfs-async-loaded');
|
||||
}).catch((e)=>error("error initializing OPFS asyncer:",e));
|
||||
}/*installAsyncProxy()*/;
|
||||
if(!self.SharedArrayBuffer){
|
||||
wPost('opfs-unavailable', "Missing SharedArrayBuffer API.",
|
||||
"The server must emit the COOP/COEP response headers to enable that.");
|
||||
}else if(!self.Atomics){
|
||||
wPost('opfs-unavailable', "Missing Atomics API.",
|
||||
"The server must emit the COOP/COEP response headers to enable that.");
|
||||
}else if(!self.FileSystemHandle ||
|
||||
!self.FileSystemDirectoryHandle ||
|
||||
!self.FileSystemFileHandle ||
|
||||
!self.FileSystemFileHandle.prototype.createSyncAccessHandle ||
|
||||
!navigator.storage.getDirectory){
|
||||
wPost('opfs-unavailable',"Missing required OPFS APIs.");
|
||||
}else{
|
||||
installAsyncProxy(self);
|
||||
}
|
||||
|
@ -1784,13 +1784,12 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
.t({
|
||||
name: 'OPFS sanity checks',
|
||||
test: async function(sqlite3){
|
||||
const opfs = sqlite3.opfs;
|
||||
const filename = 'sqlite3-tester1.db';
|
||||
const pVfs = capi.sqlite3_vfs_find('opfs');
|
||||
T.assert(pVfs);
|
||||
const unlink = (fn=filename)=>wasm.sqlite3_wasm_vfs_unlink(pVfs,fn);
|
||||
unlink();
|
||||
let db = new opfs.OpfsDb(filename);
|
||||
let db = new sqlite3.oo1.OpfsDb(filename);
|
||||
try {
|
||||
db.exec([
|
||||
'create table p(a);',
|
||||
@ -1798,7 +1797,7 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
]);
|
||||
T.assert(3 === db.selectValue('select count(*) from p'));
|
||||
db.close();
|
||||
db = new opfs.OpfsDb(filename);
|
||||
db = new sqlite3.oo1.OpfsDb(filename);
|
||||
db.exec('insert into p(a) values(4),(5),(6)');
|
||||
T.assert(6 === db.selectValue('select count(*) from p'));
|
||||
}finally{
|
||||
@ -1806,8 +1805,9 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
unlink();
|
||||
}
|
||||
|
||||
if(1){
|
||||
if(sqlite3.opfs){
|
||||
// Sanity-test sqlite3_wasm_vfs_create_file()...
|
||||
const opfs = sqlite3.opfs;
|
||||
const fSize = 1379;
|
||||
let sh;
|
||||
try{
|
||||
@ -1824,7 +1824,6 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
if(sh) await sh.close();
|
||||
unlink();
|
||||
}
|
||||
}
|
||||
|
||||
// Some sanity checks of the opfs utility functions...
|
||||
const testDir = '/sqlite3-opfs-'+opfs.randomFilename(12);
|
||||
@ -1839,6 +1838,7 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
.assert(!(await opfs.entryExists(testDir)),
|
||||
"entryExists(",testDir,") should have failed");
|
||||
}
|
||||
}
|
||||
}/*OPFS sanity checks*/)
|
||||
;/* end OPFS tests */
|
||||
|
||||
|
@ -33,9 +33,10 @@
|
||||
with <code>unlock-asap=0-1</code>.
|
||||
</p>
|
||||
<p>Achtung: if it does not start to do anything within a couple of
|
||||
seconds, check the dev console: Chrome often fails with "cannot allocate
|
||||
WasmMemory" at startup. Closing and re-opening the tab usually resolves
|
||||
it.
|
||||
seconds, check the dev console: Chrome sometimes fails to load
|
||||
the wasm module due to "cannot allocate WasmMemory." Closing and
|
||||
re-opening the tab usually resolves it, but sometimes restarting
|
||||
the browser is required.
|
||||
</p>
|
||||
<div class='input-wrapper'>
|
||||
<input type='checkbox' id='cb-log-reverse'>
|
||||
|
18
manifest
18
manifest
@ -1,5 +1,5 @@
|
||||
C Add\san\sexplicit\swarning\sabout\sthe\scurrent\sAPI-instability\sof\sthe\ssqlite3.opfs\snamespace,\swhich\smay\sneed\sto\sbe\seliminated\sbased\son\sre-thinking\sof\show\sthe\sOPFS\ssqlite3_vfs\sis\sregistered.\sComment\schanges\sonly\s-\sno\scode.
|
||||
D 2022-11-29T02:23:12.943
|
||||
C Internal\srestructuring\sof\sthe\sOPFS\ssqlite3_vfs\sin\sorder\sto\sfacilitate\scertain\sexperimentation\sand\simprove\serror\sreporting/hints\sif\sit\scannot\sbe\sactivated.\sDeprecate\sthe\sname\ssqlite3.opfs.OpfsDb,\spreferring\ssqlite3.oo1.OpfsDb\sfor\sconsistency\swith\sJsStorageDb\sand\sany\sfuture\sDB\ssubclasses.
|
||||
D 2022-11-29T05:25:08.036
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@ -505,11 +505,11 @@ F ext/wasm/api/pre-js.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f
|
||||
F ext/wasm/api/sqlite3-api-cleanup.js ecdc69dbfccfe26146f04799fcfd4a6f5790d46e7e3b9b6e9b0491f92ed8ae34
|
||||
F ext/wasm/api/sqlite3-api-glue.js 056f44b82c126358a0175e08a892d56fadfce177b0d7a0012502a6acf67ea6d5
|
||||
F ext/wasm/api/sqlite3-api-oo1.js 06ad2079368e16cb9f182c18cd37bdc3932536856dff4f60582d0ca5f6c491a8
|
||||
F ext/wasm/api/sqlite3-api-opfs.js 3cdae7e98c500f89f9468a260e2a0e1b528c845a107bf72d368e5222769214d3
|
||||
F ext/wasm/api/sqlite3-api-opfs.js 583650ffdc1452496df6b9459d018fa2aede221ae6ea0cbbbe83bd2e1bdba966
|
||||
F ext/wasm/api/sqlite3-api-prologue.js 7fce4c6a138ec3d7c285b7c125cee809e6b668d2cb0d2328a1b790b7037765bd
|
||||
F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f
|
||||
F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3
|
||||
F ext/wasm/api/sqlite3-opfs-async-proxy.js 798383f6b46fd5dac122d6e35962d25b10401ddb825b5c66df1d21e6b1d8aacc
|
||||
F ext/wasm/api/sqlite3-opfs-async-proxy.js b5dd7eda8e74e07453457925a0dd793d7785da720954e0e37e847c5c6e4d9526
|
||||
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
||||
F ext/wasm/api/sqlite3-wasm.c 8b32787a3b6bb2990cbaba2304bd5b75a9652acbc8d29909b3279019b6cbaef5
|
||||
F ext/wasm/api/sqlite3-worker1-promiser.js 0c7a9826dbf82a5ed4e4f7bf7816e825a52aff253afbf3350431f5773faf0e4b
|
||||
@ -554,8 +554,8 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
|
||||
F ext/wasm/test-opfs-vfs.js 44363db07b2a20e73b0eb1808de4400ca71b703af718d0fa6d962f15e73bf2ac
|
||||
F ext/wasm/tester1-worker.html 5ef353348c37cf2e4fd0b23da562d3275523e036260b510734e9a3239ba8c987
|
||||
F ext/wasm/tester1.c-pp.html 74aa9b31c75f12490653f814b53c3dd39f40cd3f70d6a53a716f4e8587107399
|
||||
F ext/wasm/tester1.c-pp.js 3b91f192c159088004fba6fe3441edea58421a8b88bccf3dd20978a077648d19
|
||||
F ext/wasm/tests/opfs/concurrency/index.html e8fec75ea6eddc600c8a382da7ea2579feece2263a2fb4417f2cf3e9d451744c
|
||||
F ext/wasm/tester1.c-pp.js a4b6a165aafcd3b86118efaec6b47c70fbb6c64b5ab86d21ca8c250d42617dfa
|
||||
F ext/wasm/tests/opfs/concurrency/index.html 2b1cda51d6c786102875a28eba22f0da3eecb732a5e677b0d1ecdb53546d1a62
|
||||
F ext/wasm/tests/opfs/concurrency/test.js bfc3d7e27b207f0827f12568986b8d516a744529550b449314f5c21c9e9faf4a
|
||||
F ext/wasm/tests/opfs/concurrency/worker.js 0eff027cbd3a495acb2ac94f57ca9e4d21125ab9fda07d45f3701b0efe82d450
|
||||
F ext/wasm/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd72273503ae7d5
|
||||
@ -2064,8 +2064,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 46cdd3637d6a206ad2bcf8653cc6f2c7a886a16cc7685c45967938609941a755
|
||||
R 3870d04bfd54da096e986662fe29b1c8
|
||||
P 0cb2fd14179397051a25d066256a553fc198656d5668c7010c016f2b8f495bf4
|
||||
R 115b7898d7b2ce79a9261f36a9b959d1
|
||||
U stephan
|
||||
Z ad4a8c5f45a34a10f588c0c6dc455846
|
||||
Z f4ff31d5e2499971cf67cb62dbdd0ac3
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
@ -1 +1 @@
|
||||
0cb2fd14179397051a25d066256a553fc198656d5668c7010c016f2b8f495bf4
|
||||
0c5c51f4fb04a4b90c50ec9704cfea9a3fb7d7d0ee55c1b0d4476129188217a6
|
Reference in New Issue
Block a user