mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-27 20:41:58 +03:00
Numerous cleanups in the JS bits. Removed some now-defunct wasm test files. Expose sqlite3.opfs object containing various OPFS-specific utilities.
FossilOrigin-Name: 26e625d05d9820033b23536f18ad3ddc59ed712ad507d4b0c7fe88abd15d2be8
This commit is contained in:
@ -85,9 +85,24 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
A proxy for DB class constructors. It must be called with the
|
A proxy for DB class constructors. It must be called with the
|
||||||
being-construct DB object as its "this".
|
being-construct DB object as its "this". See the DB constructor
|
||||||
|
for the argument docs. This is split into a separate function
|
||||||
|
in order to enable simple creation of special-case DB constructors,
|
||||||
|
e.g. a hypothetical LocalStorageDB or OpfsDB.
|
||||||
|
|
||||||
|
Expects to be passed a configuration object with the following
|
||||||
|
properties:
|
||||||
|
|
||||||
|
- `.filename`: the db filename. It may be a special name like ":memory:"
|
||||||
|
or "".
|
||||||
|
|
||||||
|
- `.flags`: as documented in the DB constructor.
|
||||||
|
|
||||||
|
- `.vfs`: as documented in the DB constructor.
|
||||||
|
|
||||||
|
It also accepts those as the first 3 arguments.
|
||||||
*/
|
*/
|
||||||
const dbCtorHelper = function ctor(fn=':memory:', flags='c', vfsName){
|
const dbCtorHelper = function ctor(...args){
|
||||||
if(!ctor._name2vfs){
|
if(!ctor._name2vfs){
|
||||||
// Map special filenames which we handle here (instead of in C)
|
// Map special filenames which we handle here (instead of in C)
|
||||||
// to some helpful metadata...
|
// to some helpful metadata...
|
||||||
@ -104,25 +119,33 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
filename: isWorkerThread || (()=>'session')
|
filename: isWorkerThread || (()=>'session')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if('string'!==typeof fn){
|
const opt = ctor.normalizeArgs(...args);
|
||||||
toss3("Invalid filename for DB constructor.");
|
let fn = opt.filename, vfsName = opt.vfs, flagsStr = opt.flags;
|
||||||
|
if(('string'!==typeof fn && 'number'!==typeof fn)
|
||||||
|
|| 'string'!==typeof flagsStr
|
||||||
|
|| (vfsName && ('string'!==typeof vfsName && 'number'!==typeof vfsName))){
|
||||||
|
console.error("Invalid DB ctor args",opt,arguments);
|
||||||
|
toss3("Invalid arguments for DB constructor.");
|
||||||
}
|
}
|
||||||
const vfsCheck = ctor._name2vfs[fn];
|
let fnJs = ('number'===typeof fn) ? capi.wasm.cstringToJs(fn) : fn;
|
||||||
|
const vfsCheck = ctor._name2vfs[fnJs];
|
||||||
if(vfsCheck){
|
if(vfsCheck){
|
||||||
vfsName = vfsCheck.vfs;
|
vfsName = vfsCheck.vfs;
|
||||||
fn = vfsCheck.filename(fn);
|
fn = fnJs = vfsCheck.filename(fnJs);
|
||||||
}
|
}
|
||||||
let ptr, oflags = 0;
|
let ptr, oflags = 0;
|
||||||
if( flags.indexOf('c')>=0 ){
|
if( flagsStr.indexOf('c')>=0 ){
|
||||||
oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE;
|
oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE;
|
||||||
}
|
}
|
||||||
if( flags.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE;
|
if( flagsStr.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE;
|
||||||
if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY;
|
if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY;
|
||||||
oflags |= capi.SQLITE_OPEN_EXRESCODE;
|
oflags |= capi.SQLITE_OPEN_EXRESCODE;
|
||||||
const stack = capi.wasm.scopedAllocPush();
|
const stack = capi.wasm.scopedAllocPush();
|
||||||
try {
|
try {
|
||||||
const ppDb = capi.wasm.scopedAllocPtr() /* output (sqlite3**) arg */;
|
const ppDb = capi.wasm.scopedAllocPtr() /* output (sqlite3**) arg */;
|
||||||
const pVfsName = vfsName ? capi.wasm.scopedAllocCString(vfsName) : 0;
|
const pVfsName = vfsName ? (
|
||||||
|
('number'===typeof vfsName ? vfsName : capi.wasm.scopedAllocCString(vfsName))
|
||||||
|
): 0;
|
||||||
const rc = capi.sqlite3_open_v2(fn, ppDb, oflags, pVfsName);
|
const rc = capi.sqlite3_open_v2(fn, ppDb, oflags, pVfsName);
|
||||||
ptr = capi.wasm.getPtrValue(ppDb);
|
ptr = capi.wasm.getPtrValue(ppDb);
|
||||||
checkSqlite3Rc(ptr, rc);
|
checkSqlite3Rc(ptr, rc);
|
||||||
@ -132,11 +155,36 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
}finally{
|
}finally{
|
||||||
capi.wasm.scopedAllocPop(stack);
|
capi.wasm.scopedAllocPop(stack);
|
||||||
}
|
}
|
||||||
this.filename = fn;
|
this.filename = fnJs;
|
||||||
__ptrMap.set(this, ptr);
|
__ptrMap.set(this, ptr);
|
||||||
__stmtMap.set(this, Object.create(null));
|
__stmtMap.set(this, Object.create(null));
|
||||||
__udfMap.set(this, Object.create(null));
|
__udfMap.set(this, Object.create(null));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
A helper for DB constructors. It accepts either a single
|
||||||
|
config-style object or up to 3 arguments (filename, dbOpenFlags,
|
||||||
|
dbVfsName). It returns a new object containing:
|
||||||
|
|
||||||
|
{ filename: ..., flags: ..., vfs: ... }
|
||||||
|
|
||||||
|
If passed an object, any additional properties it has are copied
|
||||||
|
as-is into the new object.
|
||||||
|
*/
|
||||||
|
dbCtorHelper.normalizeArgs = function(filename,flags = 'c',vfs = null){
|
||||||
|
const arg = {};
|
||||||
|
if(1===arguments.length && 'object'===typeof arguments[0]){
|
||||||
|
const x = arguments[0];
|
||||||
|
Object.keys(x).forEach((k)=>arg[k] = x[k]);
|
||||||
|
if(undefined===arg.flags) arg.flags = 'c';
|
||||||
|
if(undefined===arg.vfs) arg.vfs = null;
|
||||||
|
}else{
|
||||||
|
arg.filename = filename;
|
||||||
|
arg.flags = flags;
|
||||||
|
arg.vfs = vfs;
|
||||||
|
}
|
||||||
|
return arg;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The DB class provides a high-level OO wrapper around an sqlite3
|
The DB class provides a high-level OO wrapper around an sqlite3
|
||||||
@ -175,6 +223,17 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
or not at all, to use the default. If passed a value, it must
|
or not at all, to use the default. If passed a value, it must
|
||||||
be the string name of a VFS
|
be the string name of a VFS
|
||||||
|
|
||||||
|
The constructor optionally (and preferably) takes its arguments
|
||||||
|
in the form of a single configuration object with the following
|
||||||
|
properties:
|
||||||
|
|
||||||
|
- `.filename`: database file name
|
||||||
|
- `.flags`: open-mode flags
|
||||||
|
- `.vfs`: the VFS fname
|
||||||
|
|
||||||
|
The `filename` and `vfs` arguments may be either JS strings or
|
||||||
|
C-strings allocated via WASM.
|
||||||
|
|
||||||
For purposes of passing a DB instance to C-style sqlite3
|
For purposes of passing a DB instance to C-style sqlite3
|
||||||
functions, the DB object's read-only `pointer` property holds its
|
functions, the DB object's read-only `pointer` property holds its
|
||||||
`sqlite3*` pointer value. That property can also be used to check
|
`sqlite3*` pointer value. That property can also be used to check
|
||||||
@ -187,12 +246,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
the database. In this mode, only a single database is permitted
|
the database. In this mode, only a single database is permitted
|
||||||
in each storage object. This feature is experimental and subject
|
in each storage object. This feature is experimental and subject
|
||||||
to any number of changes (including outright removal). This
|
to any number of changes (including outright removal). This
|
||||||
support requires a specific build of sqlite3, the existence of
|
support requires the kvvfs sqlite3 VFS, the existence of which
|
||||||
which can be determined at runtime by checking for a non-0 return
|
can be determined at runtime by checking for a non-0 return value
|
||||||
value from sqlite3.capi.sqlite3_vfs_find("kvvfs").
|
from sqlite3.capi.sqlite3_vfs_find("kvvfs").
|
||||||
*/
|
*/
|
||||||
const DB = function ctor(fn=':memory:', flags='c', vfsName){
|
const DB = function(...args){
|
||||||
dbCtorHelper.apply(this, Array.prototype.slice.call(arguments));
|
dbCtorHelper.apply(this, args);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -361,12 +420,31 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
closed. After calling close(), `this.pointer` will resolve to
|
closed. After calling close(), `this.pointer` will resolve to
|
||||||
`undefined`, so that can be used to check whether the db
|
`undefined`, so that can be used to check whether the db
|
||||||
instance is still opened.
|
instance is still opened.
|
||||||
|
|
||||||
|
If this.onclose.before is a function then it is called before
|
||||||
|
any close-related cleanup.
|
||||||
|
|
||||||
|
If this.onclose.after is a function then it is called after the
|
||||||
|
db is closed but before auxiliary state like this.filename is
|
||||||
|
cleared.
|
||||||
|
|
||||||
|
Both onclose handlers are passed this object. If this db is not
|
||||||
|
opened, neither of the handlers are called. Any exceptions the
|
||||||
|
handlers throw are ignored because "destructors must not
|
||||||
|
throw."
|
||||||
|
|
||||||
|
Note that garbage collection of a db handle, if it happens at
|
||||||
|
all, will never trigger close(), so onclose handlers are not a
|
||||||
|
reliable way to implement close-time cleanup or maintenance of
|
||||||
|
a db.
|
||||||
*/
|
*/
|
||||||
close: function(){
|
close: function(){
|
||||||
if(this.pointer){
|
if(this.pointer){
|
||||||
|
if(this.onclose && (this.onclose.before instanceof Function)){
|
||||||
|
try{this.onclose.before(this)}
|
||||||
|
catch(e){/*ignore*/}
|
||||||
|
}
|
||||||
const pDb = this.pointer;
|
const pDb = this.pointer;
|
||||||
let s;
|
|
||||||
const that = this;
|
|
||||||
Object.keys(__stmtMap.get(this)).forEach((k,s)=>{
|
Object.keys(__stmtMap.get(this)).forEach((k,s)=>{
|
||||||
if(s && s.pointer) s.finalize();
|
if(s && s.pointer) s.finalize();
|
||||||
});
|
});
|
||||||
@ -377,6 +455,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
__stmtMap.delete(this);
|
__stmtMap.delete(this);
|
||||||
__udfMap.delete(this);
|
__udfMap.delete(this);
|
||||||
capi.sqlite3_close_v2(pDb);
|
capi.sqlite3_close_v2(pDb);
|
||||||
|
if(this.onclose && (this.onclose.after instanceof Function)){
|
||||||
|
try{this.onclose.after(this)}
|
||||||
|
catch(e){/*ignore*/}
|
||||||
|
}
|
||||||
delete this.filename;
|
delete this.filename;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -401,13 +483,13 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
Similar to this.filename but will return NULL for special names
|
Similar to this.filename but will return a falsy value for
|
||||||
like ":memory:". Not of much use until we have filesystem
|
special names like ":memory:". Throws if the DB has been
|
||||||
support. Throws if the DB has been closed. If passed an
|
closed. If passed an argument it then it will return the
|
||||||
argument it then it will return the filename of the ATTACHEd db
|
filename of the ATTACHEd db with that name, else it assumes a
|
||||||
with that name, else it assumes a name of `main`.
|
name of `main`.
|
||||||
*/
|
*/
|
||||||
fileName: function(dbName='main'){
|
getFilename: function(dbName='main'){
|
||||||
return capi.sqlite3_db_filename(affirmDbOpen(this).pointer, dbName);
|
return capi.sqlite3_db_filename(affirmDbOpen(this).pointer, dbName);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@ -1591,7 +1673,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
ooApi: "0.1"
|
ooApi: "0.1"
|
||||||
},
|
},
|
||||||
DB,
|
DB,
|
||||||
Stmt
|
Stmt,
|
||||||
|
dbCtorHelper
|
||||||
}/*oo1 object*/;
|
}/*oo1 object*/;
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -42,8 +42,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
that number will increase as the OPFS API matures).
|
that number will increase as the OPFS API matures).
|
||||||
|
|
||||||
- The OPFS features used here are only available in dedicated Worker
|
- The OPFS features used here are only available in dedicated Worker
|
||||||
threads. This file tries to detect that case and becomes a no-op
|
threads. This file tries to detect that case, resulting in a
|
||||||
if those features do not seem to be available.
|
rejected Promise if those features do not seem to be available.
|
||||||
|
|
||||||
- It requires the SharedArrayBuffer and Atomics classes, and the
|
- It requires the SharedArrayBuffer and Atomics classes, and the
|
||||||
former is only available if the HTTP server emits the so-called
|
former is only available if the HTTP server emits the so-called
|
||||||
@ -72,7 +72,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
returned Promise resolves.
|
returned Promise resolves.
|
||||||
|
|
||||||
On success, the Promise resolves to the top-most sqlite3 namespace
|
On success, the Promise resolves to the top-most sqlite3 namespace
|
||||||
object.
|
object and that object gets a new object installed in its
|
||||||
|
`opfs` property, containing several OPFS-specific utilities.
|
||||||
*/
|
*/
|
||||||
sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri){
|
sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri){
|
||||||
const options = (asyncProxyUri && 'object'===asyncProxyUri) ? asyncProxyUri : {
|
const options = (asyncProxyUri && 'object'===asyncProxyUri) ? asyncProxyUri : {
|
||||||
@ -89,6 +90,13 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
options.proxyUri = callee.defaultProxyUri;
|
options.proxyUri = callee.defaultProxyUri;
|
||||||
}
|
}
|
||||||
delete sqlite3.installOpfsVfs;
|
delete sqlite3.installOpfsVfs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Generic utilities for working with OPFS. This will get filled out
|
||||||
|
by the Promise setup and, on success, installed as sqlite3.opfs.
|
||||||
|
*/
|
||||||
|
const opfsUtil = Object.create(null);
|
||||||
|
|
||||||
const thePromise = new Promise(function(promiseResolve, promiseReject){
|
const thePromise = new Promise(function(promiseResolve, promiseReject){
|
||||||
const logPrefix = "OPFS syncer:";
|
const logPrefix = "OPFS syncer:";
|
||||||
const warn = (...args)=>{
|
const warn = (...args)=>{
|
||||||
@ -118,9 +126,8 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
const sqlite3_vfs = capi.sqlite3_vfs;
|
const sqlite3_vfs = capi.sqlite3_vfs;
|
||||||
const sqlite3_file = capi.sqlite3_file;
|
const sqlite3_file = capi.sqlite3_file;
|
||||||
const sqlite3_io_methods = capi.sqlite3_io_methods;
|
const sqlite3_io_methods = capi.sqlite3_io_methods;
|
||||||
const StructBinder = sqlite3.StructBinder;
|
|
||||||
const W = new Worker(options.proxyUri);
|
const W = new Worker(options.proxyUri);
|
||||||
const workerOrigOnError = W.onrror;
|
W._originalOnError = W.onerror /* will be restored later */;
|
||||||
W.onerror = function(err){
|
W.onerror = function(err){
|
||||||
promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons."));
|
promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons."));
|
||||||
};
|
};
|
||||||
@ -131,17 +138,37 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
This object must initially contain only cloneable or sharable
|
This object must initially contain only cloneable or sharable
|
||||||
objects. After the worker's "inited" message arrives, other types
|
objects. After the worker's "inited" message arrives, other types
|
||||||
of data may be added to it.
|
of data may be added to it.
|
||||||
|
|
||||||
|
For purposes of Atomics.wait() and Atomics.notify(), we use a
|
||||||
|
SharedArrayBuffer with one slot reserved for each of the API
|
||||||
|
proxy's methods. The sync side of the API uses Atomics.wait()
|
||||||
|
on the corresponding slot and the async side uses
|
||||||
|
Atomics.notify() on that slot.
|
||||||
|
|
||||||
|
The approach of using a single SAB to serialize comms for all
|
||||||
|
instances might(?) lead to deadlock situations in multi-db
|
||||||
|
cases. We should probably have one SAB here with a single slot
|
||||||
|
for locking a per-file initialization step and then allocate a
|
||||||
|
separate SAB like the above one for each file. That will
|
||||||
|
require a bit of acrobatics but should be feasible.
|
||||||
*/
|
*/
|
||||||
const state = Object.create(null);
|
const state = Object.create(null);
|
||||||
state.verbose = options.verbose;
|
state.verbose = options.verbose;
|
||||||
state.fileBufferSize = 1024 * 64 + 8 /* size of fileHandle.sab. 64k = max sqlite3 page size */;
|
state.fileBufferSize =
|
||||||
state.fbInt64Offset = state.fileBufferSize - 8 /*spot in fileHandle.sab to store an int64*/;
|
1024 * 64 + 8 /* size of aFileHandle.sab. 64k = max sqlite3 page
|
||||||
|
size. The additional bytes are space for
|
||||||
|
holding BigInt results, since we cannot store
|
||||||
|
those via the Atomics API (which only works on
|
||||||
|
an Int32Array). */;
|
||||||
|
state.fbInt64Offset =
|
||||||
|
state.fileBufferSize - 8 /*spot in fileHandle.sab to store an int64 result */;
|
||||||
state.opIds = Object.create(null);
|
state.opIds = Object.create(null);
|
||||||
{
|
{
|
||||||
let i = 0;
|
let i = 0;
|
||||||
state.opIds.xAccess = i++;
|
state.opIds.xAccess = i++;
|
||||||
state.opIds.xClose = i++;
|
state.opIds.xClose = i++;
|
||||||
state.opIds.xDelete = i++;
|
state.opIds.xDelete = i++;
|
||||||
|
state.opIds.xDeleteNoWait = i++;
|
||||||
state.opIds.xFileSize = i++;
|
state.opIds.xFileSize = i++;
|
||||||
state.opIds.xOpen = i++;
|
state.opIds.xOpen = i++;
|
||||||
state.opIds.xRead = i++;
|
state.opIds.xRead = i++;
|
||||||
@ -149,14 +176,8 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
state.opIds.xSync = i++;
|
state.opIds.xSync = i++;
|
||||||
state.opIds.xTruncate = i++;
|
state.opIds.xTruncate = i++;
|
||||||
state.opIds.xWrite = i++;
|
state.opIds.xWrite = i++;
|
||||||
|
state.opIds.mkdir = i++;
|
||||||
state.opSAB = new SharedArrayBuffer(i * 4/*sizeof int32*/);
|
state.opSAB = new SharedArrayBuffer(i * 4/*sizeof int32*/);
|
||||||
/* The approach of using a single SAB to serialize comms for all
|
|
||||||
instances may(?) lead to deadlock situations in multi-db
|
|
||||||
cases. We should probably have one SAB here with a single slot
|
|
||||||
for locking a per-file initialization step and then allocate a
|
|
||||||
separate SAB like the above one for each file. That will
|
|
||||||
require a bit of acrobatics but should be feasible.
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state.sq3Codes = Object.create(null);
|
state.sq3Codes = Object.create(null);
|
||||||
@ -167,15 +188,14 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
'SQLITE_IOERR_READ', 'SQLITE_IOERR_SHORT_READ',
|
'SQLITE_IOERR_READ', 'SQLITE_IOERR_SHORT_READ',
|
||||||
'SQLITE_IOERR_WRITE', 'SQLITE_IOERR_FSYNC',
|
'SQLITE_IOERR_WRITE', 'SQLITE_IOERR_FSYNC',
|
||||||
'SQLITE_IOERR_TRUNCATE', 'SQLITE_IOERR_DELETE',
|
'SQLITE_IOERR_TRUNCATE', 'SQLITE_IOERR_DELETE',
|
||||||
'SQLITE_IOERR_ACCESS', 'SQLITE_IOERR_CLOSE'
|
'SQLITE_IOERR_ACCESS', 'SQLITE_IOERR_CLOSE',
|
||||||
|
'SQLITE_IOERR_DELETE'
|
||||||
].forEach(function(k){
|
].forEach(function(k){
|
||||||
state.sq3Codes[k] = capi[k] || toss("Maintenance required: not found:",k);
|
state.sq3Codes[k] = capi[k] || toss("Maintenance required: not found:",k);
|
||||||
state.sq3Codes._reverse[capi[k]] = k;
|
state.sq3Codes._reverse[capi[k]] = k;
|
||||||
});
|
});
|
||||||
|
|
||||||
const isWorkerErrCode = (n)=>!!state.sq3Codes._reverse[n];
|
const isWorkerErrCode = (n)=>!!state.sq3Codes._reverse[n];
|
||||||
const opStore = (op,val=-1)=>Atomics.store(state.opSABView, state.opIds[op], val);
|
|
||||||
const opWait = (op,val=-1)=>Atomics.wait(state.opSABView, state.opIds[op], val);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Runs the given operation in the async worker counterpart, waits
|
Runs the given operation in the async worker counterpart, waits
|
||||||
@ -185,9 +205,9 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
given operation's signature in the async API counterpart.
|
given operation's signature in the async API counterpart.
|
||||||
*/
|
*/
|
||||||
const opRun = (op,args)=>{
|
const opRun = (op,args)=>{
|
||||||
opStore(op);
|
Atomics.store(state.opSABView, state.opIds[op], -1);
|
||||||
wMsg(op, args);
|
wMsg(op, args);
|
||||||
opWait(op);
|
Atomics.wait(state.opSABView, state.opIds[op], -1);
|
||||||
return Atomics.load(state.opSABView, state.opIds[op]);
|
return Atomics.load(state.opSABView, state.opIds[op]);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -268,7 +288,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
func with the same signature as described above.
|
func with the same signature as described above.
|
||||||
*/
|
*/
|
||||||
const installMethod = function callee(tgt, name, func){
|
const installMethod = function callee(tgt, name, func){
|
||||||
if(!(tgt instanceof StructBinder.StructType)){
|
if(!(tgt instanceof sqlite3.StructBinder.StructType)){
|
||||||
toss("Usage error: target object is-not-a StructType.");
|
toss("Usage error: target object is-not-a StructType.");
|
||||||
}
|
}
|
||||||
if(1===arguments.length){
|
if(1===arguments.length){
|
||||||
@ -429,7 +449,10 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
xDelete: function(pVfs, zName, doSyncDir){
|
xDelete: function(pVfs, zName, doSyncDir){
|
||||||
return opRun('xDelete', {filename: wasm.cstringToJs(zName), syncDir: doSyncDir});
|
opRun('xDelete', {filename: wasm.cstringToJs(zName), syncDir: doSyncDir});
|
||||||
|
/* We're ignoring errors because we cannot yet differentiate
|
||||||
|
between harmless and non-harmless failures. */
|
||||||
|
return 0;
|
||||||
},
|
},
|
||||||
xFullPathname: function(pVfs,zName,nOut,pOut){
|
xFullPathname: function(pVfs,zName,nOut,pOut){
|
||||||
/* Until/unless we have some notion of "current dir"
|
/* Until/unless we have some notion of "current dir"
|
||||||
@ -521,6 +544,65 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
for(let k of Object.keys(ioSyncWrappers)) inst(k, ioSyncWrappers[k]);
|
for(let k of Object.keys(ioSyncWrappers)) inst(k, ioSyncWrappers[k]);
|
||||||
inst = installMethod(opfsVfs);
|
inst = installMethod(opfsVfs);
|
||||||
for(let k of Object.keys(vfsSyncWrappers)) inst(k, vfsSyncWrappers[k]);
|
for(let k of Object.keys(vfsSyncWrappers)) inst(k, vfsSyncWrappers[k]);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Syncronously deletes the given OPFS filesystem entry, ignoring
|
||||||
|
any errors. As this environment has no notion of "current
|
||||||
|
directory", the given name must be an absolute path. If the 2nd
|
||||||
|
argument is truthy, deletion is recursive (use with caution!).
|
||||||
|
|
||||||
|
Returns true if the deletion succeeded and fails if it fails,
|
||||||
|
but cannot report the nature of the failure.
|
||||||
|
*/
|
||||||
|
opfsUtil.deleteEntry = function(fsEntryName,recursive){
|
||||||
|
return 0===opRun('xDelete', {filename:fsEntryName, recursive});
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
Exactly like deleteEntry() but runs asynchronously.
|
||||||
|
*/
|
||||||
|
opfsUtil.deleteEntryAsync = async function(fsEntryName,recursive){
|
||||||
|
wMsg('xDeleteNoWait', {filename: fsEntryName, recursive});
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
Synchronously creates the given directory name, recursively, in
|
||||||
|
the OPFS filesystem. Returns true if it succeeds or the
|
||||||
|
directory already exists, else false.
|
||||||
|
*/
|
||||||
|
opfsUtil.mkdir = async function(absDirName){
|
||||||
|
return 0===opRun('mkdir', absDirName);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
Synchronously checks whether the given OPFS filesystem exists,
|
||||||
|
returning true if it does, false if it doesn't.
|
||||||
|
*/
|
||||||
|
opfsUtil.entryExists = function(fsEntryName){
|
||||||
|
return 0===opRun('xAccess', fsEntryName);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Generates a random ASCII string, intended for use as a
|
||||||
|
temporary file name. Its argument is the length of the string,
|
||||||
|
defaulting to 16.
|
||||||
|
*/
|
||||||
|
opfsUtil.randomFilename = randomFilename;
|
||||||
|
|
||||||
|
if(sqlite3.oo1){
|
||||||
|
opfsUtil.OpfsDb = function(...args){
|
||||||
|
const opt = sqlite3.oo1.dbCtorHelper.normalizeArgs(...args);
|
||||||
|
opt.vfs = opfsVfs.$zName;
|
||||||
|
sqlite3.oo1.dbCtorHelper.call(this, opt);
|
||||||
|
};
|
||||||
|
opfsUtil.OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
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 = async function(){
|
const sanityCheck = async function(){
|
||||||
const scope = wasm.scopedAllocPush();
|
const scope = wasm.scopedAllocPush();
|
||||||
@ -605,7 +687,9 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
|
|||||||
warn("Running sanity checks because of opfs-sanity-check URL arg...");
|
warn("Running sanity checks because of opfs-sanity-check URL arg...");
|
||||||
sanityCheck();
|
sanityCheck();
|
||||||
}
|
}
|
||||||
W.onerror = workerOrigOnError;
|
W.onerror = W._originalOnError;
|
||||||
|
delete W._originalOnError;
|
||||||
|
sqlite3.opfs = opfsUtil;
|
||||||
promiseResolve(sqlite3);
|
promiseResolve(sqlite3);
|
||||||
log("End of OPFS sqlite3_vfs setup.", opfsVfs);
|
log("End of OPFS sqlite3_vfs setup.", opfsVfs);
|
||||||
}catch(e){
|
}catch(e){
|
||||||
|
@ -689,7 +689,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
/** State for sqlite3_web_persistent_dir(). */
|
/** State for sqlite3_web_persistent_dir(). */
|
||||||
let __persistentDir;
|
let __persistentDir;
|
||||||
/**
|
/**
|
||||||
An experiment. Do not use.
|
An experiment. Do not use in client code.
|
||||||
|
|
||||||
If the wasm environment has a persistent storage directory,
|
If the wasm environment has a persistent storage directory,
|
||||||
its path is returned by this function. If it does not then
|
its path is returned by this function. If it does not then
|
||||||
@ -699,14 +699,18 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
environment to determine whether persistence filesystem support
|
environment to determine whether persistence filesystem support
|
||||||
is available and, if it is, enables it (if needed).
|
is available and, if it is, enables it (if needed).
|
||||||
|
|
||||||
|
This function currently only recognizes the WASMFS/OPFS storage
|
||||||
|
combination. "Plain" OPFS is provided via a separate VFS which
|
||||||
|
can optionally be installed (if OPFS is available on the system)
|
||||||
|
using sqlite3.installOpfsVfs().
|
||||||
|
|
||||||
TODOs and caveats:
|
TODOs and caveats:
|
||||||
|
|
||||||
- If persistent storage is available at the root of the virtual
|
- If persistent storage is available at the root of the virtual
|
||||||
filesystem, this interface cannot currently distinguish that
|
filesystem, this interface cannot currently distinguish that
|
||||||
from the lack of persistence. That case cannot currently (with
|
from the lack of persistence. That can (in the mean time)
|
||||||
WASMFS/OPFS) happen, but it is conceivably possible in future
|
happen when using the JS-native "opfs" VFS, as opposed to the
|
||||||
environments or non-browser runtimes (none of which are yet
|
WASMFS/OPFS combination.
|
||||||
supported targets).
|
|
||||||
*/
|
*/
|
||||||
capi.sqlite3_web_persistent_dir = function(){
|
capi.sqlite3_web_persistent_dir = function(){
|
||||||
if(undefined !== __persistentDir) return __persistentDir;
|
if(undefined !== __persistentDir) return __persistentDir;
|
||||||
@ -764,6 +768,49 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
capi.wasm.exports.sqlite3_initialize();
|
capi.wasm.exports.sqlite3_initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Given an `sqlite3*` and an sqlite3_vfs name, returns a truthy
|
||||||
|
value (see below) if that db handle uses that VFS, else returns
|
||||||
|
false. If pDb is falsy then this function returns a truthy value
|
||||||
|
if the default VFS is that VFS. Results are undefined if pDb is
|
||||||
|
truthy but refers to an invalid pointer.
|
||||||
|
|
||||||
|
The 2nd argument may either be a JS string or a C-string
|
||||||
|
allocated from the wasm environment.
|
||||||
|
|
||||||
|
The truthy value it returns is a pointer to the `sqlite3_vfs`
|
||||||
|
object.
|
||||||
|
|
||||||
|
To permit safe use of this function from APIs which may be called
|
||||||
|
via the C stack (like SQL UDFs), this function does not throw: if
|
||||||
|
bad arguments cause a conversion error when passing into
|
||||||
|
wasm-space, false is returned.
|
||||||
|
*/
|
||||||
|
capi.sqlite3_web_db_uses_vfs = function(pDb,vfsName){
|
||||||
|
try{
|
||||||
|
const pK = ('number'===vfsName)
|
||||||
|
? capi.wasm.exports.sqlite3_vfs_find(vfsName)
|
||||||
|
: capi.sqlite3_vfs_find(vfsName);
|
||||||
|
if(!pK) return false;
|
||||||
|
else if(!pDb){
|
||||||
|
return capi.sqlite3_vfs_find(0)===pK ? pK : false;
|
||||||
|
}
|
||||||
|
const ppVfs = capi.wasm.allocPtr();
|
||||||
|
try{
|
||||||
|
return (
|
||||||
|
(0===capi.sqlite3_file_control(
|
||||||
|
pDb, "main", capi.SQLITE_FCNTL_VFS_POINTER, ppVfs
|
||||||
|
)) && (capi.wasm.getPtrValue(ppVfs) === pK)
|
||||||
|
) ? pK : false;
|
||||||
|
}finally{
|
||||||
|
capi.wasm.dealloc(ppVfs);
|
||||||
|
}
|
||||||
|
}catch(e){
|
||||||
|
/* Ignore - probably bad args to a wasm-bound function. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if( self.window===self ){
|
if( self.window===self ){
|
||||||
/* Features specific to the main window thread... */
|
/* Features specific to the main window thread... */
|
||||||
|
|
||||||
@ -812,7 +859,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
/**
|
/**
|
||||||
This routine guesses the approximate amount of
|
This routine guesses the approximate amount of
|
||||||
window.localStorage and/or window.sessionStorage in use by the
|
window.localStorage and/or window.sessionStorage in use by the
|
||||||
kvvfs database backend. Its argument must be one of
|
kvvfs database backend. Its argument must be one of
|
||||||
('session', 'local', ''). In the first two cases, only
|
('session', 'local', ''). In the first two cases, only
|
||||||
sessionStorage resp. localStorage is counted. If it's an empty
|
sessionStorage resp. localStorage is counted. If it's an empty
|
||||||
string (the default) then both are counted. Only storage keys
|
string (the default) then both are counted. Only storage keys
|
||||||
@ -842,34 +889,6 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
return sz * 2 /* because JS uses UC16 encoding */;
|
return sz * 2 /* because JS uses UC16 encoding */;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
Given an `sqlite3*`, returns a truthy value (see below) if that
|
|
||||||
db handle uses the "kvvfs" VFS, else returns false. If pDb is
|
|
||||||
NULL then this function returns true if the default VFS is
|
|
||||||
"kvvfs". Results are undefined if pDb is truthy but refers to
|
|
||||||
an invalid pointer.
|
|
||||||
|
|
||||||
The truthy value it returns is a pointer to the kvvfs
|
|
||||||
`sqlite3_vfs` object.
|
|
||||||
*/
|
|
||||||
capi.sqlite3_web_db_is_kvvfs = function(pDb){
|
|
||||||
const pK = capi.sqlite3_vfs_find("kvvfs");
|
|
||||||
if(!pK) return false;
|
|
||||||
else if(!pDb){
|
|
||||||
return capi.sqlite3_vfs_find(0) && pK;
|
|
||||||
}
|
|
||||||
const scope = capi.wasm.scopedAllocPush();
|
|
||||||
try{
|
|
||||||
const ppVfs = capi.wasm.scopedAllocPtr();
|
|
||||||
return (
|
|
||||||
(0===capi.sqlite3_file_control(
|
|
||||||
pDb, "main", capi.SQLITE_FCNTL_VFS_POINTER, ppVfs
|
|
||||||
)) && (capi.wasm.getPtrValue(ppVfs) === pK)
|
|
||||||
) ? pK : false;
|
|
||||||
}finally{
|
|
||||||
capi.wasm.scopedAllocPop(scope);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}/* main-window-only bits */
|
}/* main-window-only bits */
|
||||||
|
|
||||||
/* The remainder of the API will be set up in later steps. */
|
/* The remainder of the API will be set up in later steps. */
|
||||||
|
@ -371,10 +371,14 @@ sqlite3.initWorker1API = function(){
|
|||||||
close: function(db,alsoUnlink){
|
close: function(db,alsoUnlink){
|
||||||
if(db){
|
if(db){
|
||||||
delete this.dbs[getDbId(db)];
|
delete this.dbs[getDbId(db)];
|
||||||
const filename = db.fileName();
|
const filename = db.getFilename();
|
||||||
db.close();
|
db.close();
|
||||||
if(db===this.defaultDb) this.defaultDb = undefined;
|
if(db===this.defaultDb) this.defaultDb = undefined;
|
||||||
if(alsoUnlink && filename){
|
if(alsoUnlink && filename){
|
||||||
|
/* This isn't necessarily correct: the db might be using a
|
||||||
|
VFS other than the default. How do we best resolve this
|
||||||
|
without having to special-case the kvvfs and opfs
|
||||||
|
VFSes? */
|
||||||
sqlite3.capi.wasm.sqlite3_wasm_vfs_unlink(filename);
|
sqlite3.capi.wasm.sqlite3_wasm_vfs_unlink(filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,8 +48,6 @@
|
|||||||
experimenting with WASMFS/OPFS-based persistence. Maintenance
|
experimenting with WASMFS/OPFS-based persistence. Maintenance
|
||||||
reminder: we cannot currently (2022-09-15) load WASMFS in a
|
reminder: we cannot currently (2022-09-15) load WASMFS in a
|
||||||
worker due to an Emscripten limitation.</li>
|
worker due to an Emscripten limitation.</li>
|
||||||
<li><a href='scratchpad-opfs-worker.html'>scratchpad-opfs-worker</a>:
|
|
||||||
experimenting with OPFS from a Worker thread (without WASMFS).</li>
|
|
||||||
<li><a href='test-opfs-vfs.html'>test-opfs-vfs</a>
|
<li><a href='test-opfs-vfs.html'>test-opfs-vfs</a>
|
||||||
(<a href='test-opfs-vfs.html?opfs-sanity-check&opfs-verbose'>same
|
(<a href='test-opfs-vfs.html?opfs-sanity-check&opfs-verbose'>same
|
||||||
with verbose output and sanity-checking tests</a>) is an
|
with verbose output and sanity-checking tests</a>) is an
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en-us">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
|
|
||||||
<link rel="stylesheet" href="common/emscripten.css"/>
|
|
||||||
<link rel="stylesheet" href="common/testing.css"/>
|
|
||||||
<title>sqlite3 OPFS Worker-thread Scratchpad</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header id='titlebar'><span>sqlite3 OPFS Worker-thread Scratchpad</span></header>
|
|
||||||
<p>All stuff on this page happens in the dev console.</p>
|
|
||||||
<hr>
|
|
||||||
<div id='test-output'></div>
|
|
||||||
<script src="scratchpad-opfs-worker.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
2022-05-22
|
|
||||||
|
|
||||||
The author disclaims copyright to this source code. In place of a
|
|
||||||
legal notice, here is a blessing:
|
|
||||||
|
|
||||||
* May you do good and not evil.
|
|
||||||
* May you find forgiveness for yourself and forgive others.
|
|
||||||
* May you share freely, never taking more than you give.
|
|
||||||
|
|
||||||
***********************************************************************
|
|
||||||
|
|
||||||
A basic test script for sqlite3-api.js. This file must be run in
|
|
||||||
main JS thread. It will load sqlite3.js in a worker thread.
|
|
||||||
*/
|
|
||||||
'use strict';
|
|
||||||
(function(){
|
|
||||||
const toss = function(...args){throw new Error(args.join(' '))};
|
|
||||||
const eOutput = document.querySelector('#test-output');
|
|
||||||
const logHtml = function(cssClass,...args){
|
|
||||||
if(Array.isArray(args[0])) args = args[0];
|
|
||||||
const ln = document.createElement('div');
|
|
||||||
if(cssClass) ln.classList.add(cssClass);
|
|
||||||
ln.append(document.createTextNode(args.join(' ')));
|
|
||||||
eOutput.append(ln);
|
|
||||||
};
|
|
||||||
const log = function(...args){
|
|
||||||
logHtml('',...args);
|
|
||||||
};
|
|
||||||
const error = function(...args){
|
|
||||||
logHtml('error',...args);
|
|
||||||
};
|
|
||||||
const warn = function(...args){
|
|
||||||
logHtml('warning',...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
const W = new Worker("scratchpad-opfs-worker2.js");
|
|
||||||
W.onmessage = function(ev){
|
|
||||||
ev = ev.data;
|
|
||||||
const d = ev.data;
|
|
||||||
switch(ev.type){
|
|
||||||
case 'stdout': log(d); break;
|
|
||||||
case 'stderr': error(d); break;
|
|
||||||
default: warn("Unhandled message type:",ev); break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})();
|
|
@ -1,494 +0,0 @@
|
|||||||
/*
|
|
||||||
2022-05-22
|
|
||||||
|
|
||||||
The author disclaims copyright to this source code. In place of a
|
|
||||||
legal notice, here is a blessing:
|
|
||||||
|
|
||||||
* May you do good and not evil.
|
|
||||||
* May you find forgiveness for yourself and forgive others.
|
|
||||||
* May you share freely, never taking more than you give.
|
|
||||||
|
|
||||||
***********************************************************************
|
|
||||||
|
|
||||||
An experiment for wasmfs/opfs. This file MUST be in the same dir as
|
|
||||||
the sqlite3.js emscripten module or that module won't be able to
|
|
||||||
resolve the relative URIs (importScript()'s relative URI handling
|
|
||||||
is, quite frankly, broken).
|
|
||||||
*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const toss = function(...args){throw new Error(args.join(' '))};
|
|
||||||
/**
|
|
||||||
Posts a message in the form {type,data} unless passed more than 2
|
|
||||||
args, in which case it posts {type, data:[arg1...argN]}.
|
|
||||||
*/
|
|
||||||
const wMsg = function(type,data){
|
|
||||||
postMessage({
|
|
||||||
type,
|
|
||||||
data: arguments.length<3
|
|
||||||
? data
|
|
||||||
: Array.prototype.slice.call(arguments,1)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const stdout = function(...args){
|
|
||||||
wMsg('stdout',args);
|
|
||||||
console.log(...args);
|
|
||||||
};
|
|
||||||
const stderr = function(...args){
|
|
||||||
wMsg('stderr',args);
|
|
||||||
console.error(...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
const log = console.log.bind(console);
|
|
||||||
const warn = console.warn.bind(console);
|
|
||||||
const error = console.error.bind(console);
|
|
||||||
|
|
||||||
|
|
||||||
const initOpfsBits = async function(sqlite3){
|
|
||||||
if(!self.importScripts || !self.FileSystemFileHandle){
|
|
||||||
//|| !self.FileSystemFileHandle.prototype.createSyncAccessHandle){
|
|
||||||
// ^^^ sync API is not required with WASMFS/OPFS backend.
|
|
||||||
warn("OPFS is not available in this environment.");
|
|
||||||
return;
|
|
||||||
}else if(!sqlite3.capi.wasm.bigIntEnabled){
|
|
||||||
error("OPFS requires BigInt support but sqlite3.capi.wasm.bigIntEnabled is false.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//warn('self.FileSystemFileHandle =',self.FileSystemFileHandle);
|
|
||||||
//warn('self.FileSystemFileHandle.prototype =',self.FileSystemFileHandle.prototype);
|
|
||||||
const capi = sqlite3.capi,
|
|
||||||
wasm = capi.wasm;
|
|
||||||
const sqlite3_vfs = capi.sqlite3_vfs
|
|
||||||
|| toss("Missing sqlite3.capi.sqlite3_vfs object.");
|
|
||||||
const sqlite3_file = capi.sqlite3_file
|
|
||||||
|| toss("Missing sqlite3.capi.sqlite3_file object.");
|
|
||||||
const sqlite3_io_methods = capi.sqlite3_io_methods
|
|
||||||
|| toss("Missing sqlite3.capi.sqlite3_io_methods object.");
|
|
||||||
const StructBinder = sqlite3.StructBinder || toss("Missing sqlite3.StructBinder.");
|
|
||||||
const debug = console.debug.bind(console),
|
|
||||||
log = console.log.bind(console);
|
|
||||||
warn("UNDER CONSTRUCTION: setting up OPFS VFS...");
|
|
||||||
|
|
||||||
const pDVfs = capi.sqlite3_vfs_find(null)/*pointer to default VFS*/;
|
|
||||||
const dVfs = pDVfs
|
|
||||||
? new sqlite3_vfs(pDVfs)
|
|
||||||
: null /* dVfs will be null when sqlite3 is built with
|
|
||||||
SQLITE_OS_OTHER. Though we cannot currently handle
|
|
||||||
that case, the hope is to eventually be able to. */;
|
|
||||||
const oVfs = new sqlite3_vfs();
|
|
||||||
const oIom = new sqlite3_io_methods();
|
|
||||||
oVfs.$iVersion = 2/*yes, two*/;
|
|
||||||
oVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof;
|
|
||||||
oVfs.$mxPathname = 1024/*sure, why not?*/;
|
|
||||||
oVfs.$zName = wasm.allocCString("opfs");
|
|
||||||
oVfs.ondispose = [
|
|
||||||
'$zName', oVfs.$zName,
|
|
||||||
'cleanup dVfs', ()=>(dVfs ? dVfs.dispose() : null)
|
|
||||||
];
|
|
||||||
if(dVfs){
|
|
||||||
oVfs.$xSleep = dVfs.$xSleep;
|
|
||||||
oVfs.$xRandomness = dVfs.$xRandomness;
|
|
||||||
}
|
|
||||||
// All C-side memory of oVfs is zeroed out, but just to be explicit:
|
|
||||||
oVfs.$xDlOpen = oVfs.$xDlError = oVfs.$xDlSym = oVfs.$xDlClose = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Pedantic sidebar about oVfs.ondispose: the entries in that array
|
|
||||||
are items to clean up when oVfs.dispose() is called, but in this
|
|
||||||
environment it will never be called. The VFS instance simply
|
|
||||||
hangs around until the WASM module instance is cleaned up. We
|
|
||||||
"could" _hypothetically_ clean it up by "importing" an
|
|
||||||
sqlite3_os_end() impl into the wasm build, but the shutdown order
|
|
||||||
of the wasm engine and the JS one are undefined so there is no
|
|
||||||
guaranty that the oVfs instance would be available in one
|
|
||||||
environment or the other when sqlite3_os_end() is called (_if_ it
|
|
||||||
gets called at all in a wasm build, which is undefined).
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
Installs a StructBinder-bound function pointer member of the
|
|
||||||
given name and function in the given StructType target object.
|
|
||||||
It creates a WASM proxy for the given function and arranges for
|
|
||||||
that proxy to be cleaned up when tgt.dispose() is called. Throws
|
|
||||||
on the slightest hint of error (e.g. tgt is-not-a StructType,
|
|
||||||
name does not map to a struct-bound member, etc.).
|
|
||||||
|
|
||||||
Returns a proxy for this function which is bound to tgt and takes
|
|
||||||
2 args (name,func). That function returns the same thing,
|
|
||||||
permitting calls to be chained.
|
|
||||||
|
|
||||||
If called with only 1 arg, it has no side effects but returns a
|
|
||||||
func with the same signature as described above.
|
|
||||||
*/
|
|
||||||
const installMethod = function callee(tgt, name, func){
|
|
||||||
if(!(tgt instanceof StructBinder.StructType)){
|
|
||||||
toss("Usage error: target object is-not-a StructType.");
|
|
||||||
}
|
|
||||||
if(1===arguments.length){
|
|
||||||
return (n,f)=>callee(tgt,n,f);
|
|
||||||
}
|
|
||||||
if(!callee.argcProxy){
|
|
||||||
callee.argcProxy = function(func,sig){
|
|
||||||
return function(...args){
|
|
||||||
if(func.length!==arguments.length){
|
|
||||||
toss("Argument mismatch. Native signature is:",sig);
|
|
||||||
}
|
|
||||||
return func.apply(this, args);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
callee.removeFuncList = function(){
|
|
||||||
if(this.ondispose.__removeFuncList){
|
|
||||||
this.ondispose.__removeFuncList.forEach(
|
|
||||||
(v,ndx)=>{
|
|
||||||
if('number'===typeof v){
|
|
||||||
try{wasm.uninstallFunction(v)}
|
|
||||||
catch(e){/*ignore*/}
|
|
||||||
}
|
|
||||||
/* else it's a descriptive label for the next number in
|
|
||||||
the list. */
|
|
||||||
}
|
|
||||||
);
|
|
||||||
delete this.ondispose.__removeFuncList;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}/*static init*/
|
|
||||||
const sigN = tgt.memberSignature(name);
|
|
||||||
if(sigN.length<2){
|
|
||||||
toss("Member",name," is not a function pointer. Signature =",sigN);
|
|
||||||
}
|
|
||||||
const memKey = tgt.memberKey(name);
|
|
||||||
//log("installMethod",tgt, name, sigN);
|
|
||||||
const fProxy = 1
|
|
||||||
// We can remove this proxy middle-man once the VFS is working
|
|
||||||
? callee.argcProxy(func, sigN)
|
|
||||||
: func;
|
|
||||||
const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true));
|
|
||||||
tgt[memKey] = pFunc;
|
|
||||||
if(!tgt.ondispose) tgt.ondispose = [];
|
|
||||||
if(!tgt.ondispose.__removeFuncList){
|
|
||||||
tgt.ondispose.push('ondispose.__removeFuncList handler',
|
|
||||||
callee.removeFuncList);
|
|
||||||
tgt.ondispose.__removeFuncList = [];
|
|
||||||
}
|
|
||||||
tgt.ondispose.__removeFuncList.push(memKey, pFunc);
|
|
||||||
return (n,f)=>callee(tgt, n, f);
|
|
||||||
}/*installMethod*/;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Map of sqlite3_file pointers to OPFS handles.
|
|
||||||
*/
|
|
||||||
const __opfsHandles = Object.create(null);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Generates a random ASCII string len characters long, intended for
|
|
||||||
use as a temporary file name.
|
|
||||||
*/
|
|
||||||
const randomFilename = function f(len=16){
|
|
||||||
if(!f._chars){
|
|
||||||
f._chars = "abcdefghijklmnopqrstuvwxyz"+
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+
|
|
||||||
"012346789";
|
|
||||||
f._n = f._chars.length;
|
|
||||||
}
|
|
||||||
const a = [];
|
|
||||||
let i = 0;
|
|
||||||
for( ; i < len; ++i){
|
|
||||||
const ndx = Math.random() * (f._n * 64) % f._n | 0;
|
|
||||||
a[i] = f._chars[ndx];
|
|
||||||
}
|
|
||||||
return a.join('');
|
|
||||||
};
|
|
||||||
|
|
||||||
const rootDir = await navigator.storage.getDirectory();
|
|
||||||
log("rootDir =",rootDir);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
// Set up OPFS VFS methods...
|
|
||||||
let inst = installMethod(oVfs);
|
|
||||||
inst('xOpen', function(pVfs, zName, pFile, flags, pOutFlags){
|
|
||||||
const f = new sqlite3_file(pFile);
|
|
||||||
f.$pMethods = oIom.pointer;
|
|
||||||
__opfsHandles[pFile] = f;
|
|
||||||
f.opfsHandle = null /* TODO */;
|
|
||||||
if(flags & capi.SQLITE_OPEN_DELETEONCLOSE){
|
|
||||||
f.deleteOnClose = true;
|
|
||||||
}
|
|
||||||
f.filename = zName ? wasm.cstringToJs(zName) : 'sqlite3-xOpen-'+randomFilename();
|
|
||||||
error("OPFS sqlite3_vfs::xOpen is not yet full implemented.");
|
|
||||||
return capi.SQLITE_IOERR;
|
|
||||||
})
|
|
||||||
('xFullPathname', function(pVfs,zName,nOut,pOut){
|
|
||||||
/* Until/unless we have some notion of "current dir"
|
|
||||||
in OPFS, simply copy zName to pOut... */
|
|
||||||
const i = wasm.cstrncpy(pOut, zName, nOut);
|
|
||||||
return i<nOut ? 0 : capi.SQLITE_CANTOPEN
|
|
||||||
/*CANTOPEN is required by the docs but SQLITE_RANGE would be a closer match*/;
|
|
||||||
})
|
|
||||||
('xAccess', function(pVfs,zName,flags,pOut){
|
|
||||||
error("OPFS sqlite3_vfs::xAccess is not yet implemented.");
|
|
||||||
let fileExists = 0;
|
|
||||||
switch(flags){
|
|
||||||
case capi.SQLITE_ACCESS_EXISTS: break;
|
|
||||||
case capi.SQLITE_ACCESS_READWRITE: break;
|
|
||||||
case capi.SQLITE_ACCESS_READ/*docs say this is never used*/:
|
|
||||||
default:
|
|
||||||
error("Unexpected flags value for sqlite3_vfs::xAccess():",flags);
|
|
||||||
return capi.SQLITE_MISUSE;
|
|
||||||
}
|
|
||||||
wasm.setMemValue(pOut, fileExists, 'i32');
|
|
||||||
return 0;
|
|
||||||
})
|
|
||||||
('xDelete', function(pVfs, zName, doSyncDir){
|
|
||||||
error("OPFS sqlite3_vfs::xDelete is not yet implemented.");
|
|
||||||
// https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/file_system_access/file_system_handle.idl
|
|
||||||
// ==> remove()
|
|
||||||
return capi.SQLITE_IOERR;
|
|
||||||
})
|
|
||||||
('xGetLastError', function(pVfs,nOut,pOut){
|
|
||||||
debug("OPFS sqlite3_vfs::xGetLastError() has nothing sensible to return.");
|
|
||||||
return 0;
|
|
||||||
})
|
|
||||||
('xCurrentTime', function(pVfs,pOut){
|
|
||||||
/* If it turns out that we need to adjust for timezone, see:
|
|
||||||
https://stackoverflow.com/a/11760121/1458521 */
|
|
||||||
wasm.setMemValue(pOut, 2440587.5 + (new Date().getTime()/86400000),
|
|
||||||
'double');
|
|
||||||
return 0;
|
|
||||||
})
|
|
||||||
('xCurrentTimeInt64',function(pVfs,pOut){
|
|
||||||
// TODO: confirm that this calculation is correct
|
|
||||||
wasm.setMemValue(pOut, (2440587.5 * 86400000) + new Date().getTime(),
|
|
||||||
'i64');
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
if(!oVfs.$xSleep){
|
|
||||||
inst('xSleep', function(pVfs,ms){
|
|
||||||
error("sqlite3_vfs::xSleep(",ms,") cannot be implemented from "+
|
|
||||||
"JS and we have no default VFS to copy the impl from.");
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if(!oVfs.$xRandomness){
|
|
||||||
inst('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;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
// Set up OPFS sqlite3_io_methods...
|
|
||||||
inst = installMethod(oIom);
|
|
||||||
inst('xClose', async function(pFile){
|
|
||||||
warn("xClose(",arguments,") uses await");
|
|
||||||
const f = __opfsHandles[pFile];
|
|
||||||
delete __opfsHandles[pFile];
|
|
||||||
if(f.opfsHandle){
|
|
||||||
await f.opfsHandle.close();
|
|
||||||
if(f.deleteOnClose){
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.dispose();
|
|
||||||
return 0;
|
|
||||||
})
|
|
||||||
('xRead', /*i(ppij)*/function(pFile,pDest,n,offset){
|
|
||||||
/* int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) */
|
|
||||||
try {
|
|
||||||
const f = __opfsHandles[pFile];
|
|
||||||
const heap = wasm.heap8u();
|
|
||||||
const b = new Uint8Array(heap.buffer, pDest, n);
|
|
||||||
const nRead = f.opfsHandle.read(b, {at: offset});
|
|
||||||
if(nRead<n){
|
|
||||||
// MUST zero-fill short reads (per the docs)
|
|
||||||
heap.fill(0, dest + nRead, n - nRead);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
error("xRead(",arguments,") failed:",e);
|
|
||||||
return capi.SQLITE_IOERR_READ;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
('xWrite', /*i(ppij)*/function(pFile,pSrc,n,offset){
|
|
||||||
/* int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst) */
|
|
||||||
try {
|
|
||||||
const f = __opfsHandles[pFile];
|
|
||||||
const b = new Uint8Array(wasm.heap8u().buffer, pSrc, n);
|
|
||||||
const nOut = f.opfsHandle.write(b, {at: offset});
|
|
||||||
if(nOut<n){
|
|
||||||
error("xWrite(",arguments,") short write!");
|
|
||||||
return capi.SQLITE_IOERR_WRITE;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
error("xWrite(",arguments,") failed:",e);
|
|
||||||
return capi.SQLITE_IOERR_WRITE;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
('xTruncate', /*i(pj)*/async function(pFile,sz){
|
|
||||||
/* int (*xTruncate)(sqlite3_file*, sqlite3_int64 size) */
|
|
||||||
try{
|
|
||||||
warn("xTruncate(",arguments,") uses await");
|
|
||||||
const f = __opfsHandles[pFile];
|
|
||||||
await f.opfsHandle.truncate(sz);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
catch(e){
|
|
||||||
error("xTruncate(",arguments,") failed:",e);
|
|
||||||
return capi.SQLITE_IOERR_TRUNCATE;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
('xSync', /*i(pi)*/async function(pFile,flags){
|
|
||||||
/* int (*xSync)(sqlite3_file*, int flags) */
|
|
||||||
try {
|
|
||||||
warn("xSync(",arguments,") uses await");
|
|
||||||
const f = __opfsHandles[pFile];
|
|
||||||
await f.opfsHandle.flush();
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
error("xSync(",arguments,") failed:",e);
|
|
||||||
return capi.SQLITE_IOERR_SYNC;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
('xFileSize', /*i(pp)*/async function(pFile,pSz){
|
|
||||||
/* int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize) */
|
|
||||||
try {
|
|
||||||
warn("xFileSize(",arguments,") uses await");
|
|
||||||
const f = __opfsHandles[pFile];
|
|
||||||
const fsz = await f.opfsHandle.getSize();
|
|
||||||
capi.wasm.setMemValue(pSz, fsz,'i64');
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
error("xFileSize(",arguments,") failed:",e);
|
|
||||||
return capi.SQLITE_IOERR_SEEK;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
('xLock', /*i(pi)*/function(pFile,lockType){
|
|
||||||
/* int (*xLock)(sqlite3_file*, int) */
|
|
||||||
// Opening a handle locks it automatically.
|
|
||||||
warn("xLock(",arguments,") is a no-op");
|
|
||||||
return 0;
|
|
||||||
})
|
|
||||||
('xUnlock', /*i(pi)*/function(pFile,lockType){
|
|
||||||
/* int (*xUnlock)(sqlite3_file*, int) */
|
|
||||||
// Opening a handle locks it automatically.
|
|
||||||
warn("xUnlock(",arguments,") is a no-op");
|
|
||||||
return 0;
|
|
||||||
})
|
|
||||||
('xCheckReservedLock', /*i(pp)*/function(pFile,pOut){
|
|
||||||
/* int (*xCheckReservedLock)(sqlite3_file*, int *pResOut) */
|
|
||||||
// Exclusive lock is automatically acquired when opened
|
|
||||||
warn("xCheckReservedLock(",arguments,") is a no-op");
|
|
||||||
wasm.setMemValue(pOut,1,'i32');
|
|
||||||
return 0;
|
|
||||||
})
|
|
||||||
('xFileControl', /*i(pip)*/function(pFile,op,pArg){
|
|
||||||
/* int (*xFileControl)(sqlite3_file*, int op, void *pArg) */
|
|
||||||
debug("xFileControl(",arguments,") is a no-op");
|
|
||||||
return capi.SQLITE_NOTFOUND;
|
|
||||||
})
|
|
||||||
('xDeviceCharacteristics',/*i(p)*/function(pFile){
|
|
||||||
/* int (*xDeviceCharacteristics)(sqlite3_file*) */
|
|
||||||
debug("xDeviceCharacteristics(",pFile,")");
|
|
||||||
return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
|
|
||||||
});
|
|
||||||
// xSectorSize may be NULL
|
|
||||||
//('xSectorSize', function(pFile){
|
|
||||||
// /* int (*xSectorSize)(sqlite3_file*) */
|
|
||||||
// log("xSectorSize(",pFile,")");
|
|
||||||
// return 4096 /* ==> SQLITE_DEFAULT_SECTOR_SIZE */;
|
|
||||||
//})
|
|
||||||
|
|
||||||
const rc = capi.sqlite3_vfs_register(oVfs.pointer, 0);
|
|
||||||
if(rc){
|
|
||||||
oVfs.dispose();
|
|
||||||
toss("sqlite3_vfs_register(OPFS) failed with rc",rc);
|
|
||||||
}
|
|
||||||
capi.sqlite3_vfs_register.addReference(oVfs, oIom);
|
|
||||||
warn("End of (very incomplete) OPFS setup.", oVfs);
|
|
||||||
//oVfs.dispose()/*only because we can't yet do anything with it*/;
|
|
||||||
|
|
||||||
}/*initOpfsBits()*/;
|
|
||||||
|
|
||||||
(async function(){
|
|
||||||
importScripts('sqlite3.js');
|
|
||||||
|
|
||||||
const test1 = function(db){
|
|
||||||
db.exec("create table if not exists t(a);")
|
|
||||||
.transaction(function(db){
|
|
||||||
db.prepare("insert into t(a) values(?)")
|
|
||||||
.bind(new Date().getTime())
|
|
||||||
.stepFinalize();
|
|
||||||
stdout("Number of values in table t:",
|
|
||||||
db.selectValue("select count(*) from t"));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const runTests = async function(Module){
|
|
||||||
//stdout("Module",Module);
|
|
||||||
self._MODULE = Module /* this is only to facilitate testing from the console */;
|
|
||||||
const sqlite3 = Module.sqlite3,
|
|
||||||
capi = sqlite3.capi,
|
|
||||||
oo = sqlite3.oo1,
|
|
||||||
wasm = capi.wasm;
|
|
||||||
stdout("Loaded sqlite3:",capi.sqlite3_libversion(), capi.sqlite3_sourceid());
|
|
||||||
|
|
||||||
if(1){
|
|
||||||
let errCount = 0;
|
|
||||||
[
|
|
||||||
'FileSystemHandle', 'FileSystemFileHandle', 'FileSystemDirectoryHandle',
|
|
||||||
'FileSystemSyncAccessHandle'
|
|
||||||
].forEach(function(n){
|
|
||||||
const f = self[n];
|
|
||||||
if(f){
|
|
||||||
warn(n,f);
|
|
||||||
warn(n+'.prototype',f.prototype);
|
|
||||||
}else{
|
|
||||||
stderr("MISSING",n);
|
|
||||||
++errCount;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if(errCount) return;
|
|
||||||
}
|
|
||||||
warn('self',self);
|
|
||||||
await initOpfsBits(sqlite3);
|
|
||||||
|
|
||||||
if(1) return;
|
|
||||||
|
|
||||||
let persistentDir;
|
|
||||||
if(1){
|
|
||||||
persistentDir = '';
|
|
||||||
}else{
|
|
||||||
persistentDir = capi.sqlite3_web_persistent_dir();
|
|
||||||
if(persistentDir){
|
|
||||||
stderr("Persistent storage dir:",persistentDir);
|
|
||||||
}else{
|
|
||||||
stderr("No persistent storage available.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const startTime = performance.now();
|
|
||||||
let db;
|
|
||||||
try {
|
|
||||||
db = new oo.DB(persistentDir+'/foo.db');
|
|
||||||
stdout("DB filename:",db.filename,db.fileName());
|
|
||||||
const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>',
|
|
||||||
banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<';
|
|
||||||
[
|
|
||||||
test1
|
|
||||||
].forEach((f)=>{
|
|
||||||
const n = performance.now();
|
|
||||||
stdout(banner1,"Running",f.name+"()...");
|
|
||||||
f(db, sqlite3, Module);
|
|
||||||
stdout(banner2,f.name+"() took ",(performance.now() - n),"ms");
|
|
||||||
});
|
|
||||||
}finally{
|
|
||||||
if(db) db.close();
|
|
||||||
}
|
|
||||||
stdout("Total test time:",(performance.now() - startTime),"ms");
|
|
||||||
};
|
|
||||||
|
|
||||||
sqlite3InitModule(self.sqlite3TestModule).then(runTests);
|
|
||||||
})();
|
|
@ -52,7 +52,7 @@
|
|||||||
let db;
|
let db;
|
||||||
try {
|
try {
|
||||||
db = new oo.DB(persistentDir+'/foo.db');
|
db = new oo.DB(persistentDir+'/foo.db');
|
||||||
stdout("DB filename:",db.filename,db.fileName());
|
stdout("DB filename:",db.filename,db.getFilename());
|
||||||
const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>',
|
const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>',
|
||||||
banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<';
|
banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<';
|
||||||
[
|
[
|
||||||
|
@ -164,7 +164,7 @@ const vfsAsyncImpls = {
|
|||||||
storeAndNotify(opName, state.sq3Codes.SQLITE_NOFOUND);
|
storeAndNotify(opName, state.sq3Codes.SQLITE_NOFOUND);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
xDelete: async function({filename, syncDir/*ignored*/}){
|
xDeleteNoWait: async function({filename, syncDir, recursive = false}){
|
||||||
/* The syncDir flag is, for purposes of the VFS API's semantics,
|
/* The syncDir flag is, for purposes of the VFS API's semantics,
|
||||||
ignored here. However, if it has the value 0x1234 then: after
|
ignored here. However, if it has the value 0x1234 then: after
|
||||||
deleting the given file, recursively try to delete any empty
|
deleting the given file, recursively try to delete any empty
|
||||||
@ -178,12 +178,13 @@ const vfsAsyncImpls = {
|
|||||||
is false.
|
is false.
|
||||||
*/
|
*/
|
||||||
log("xDelete(",arguments[0],")");
|
log("xDelete(",arguments[0],")");
|
||||||
|
let rc = 0;
|
||||||
try {
|
try {
|
||||||
while(filename){
|
while(filename){
|
||||||
const [hDir, filenamePart] = await getDirForPath(filename, false);
|
const [hDir, filenamePart] = await getDirForPath(filename, false);
|
||||||
//log("Removing:",hDir, filenamePart);
|
//log("Removing:",hDir, filenamePart);
|
||||||
if(!filenamePart) break;
|
if(!filenamePart) break;
|
||||||
await hDir.removeEntry(filenamePart);
|
await hDir.removeEntry(filenamePart, {recursive});
|
||||||
if(0x1234 !== syncDir) break;
|
if(0x1234 !== syncDir) break;
|
||||||
filename = getResolvedPath(filename, true);
|
filename = getResolvedPath(filename, true);
|
||||||
filename.pop();
|
filename.pop();
|
||||||
@ -193,8 +194,23 @@ const vfsAsyncImpls = {
|
|||||||
/* Ignoring: _presumably_ the file can't be found or a dir is
|
/* Ignoring: _presumably_ the file can't be found or a dir is
|
||||||
not empty. */
|
not empty. */
|
||||||
//error("Delete failed",filename, e.message);
|
//error("Delete failed",filename, e.message);
|
||||||
|
rc = state.sq3Codes.SQLITE_IOERR_DELETE;
|
||||||
}
|
}
|
||||||
storeAndNotify('xDelete', 0);
|
return rc;
|
||||||
|
},
|
||||||
|
xDelete: async function(...args){
|
||||||
|
const rc = await vfsAsyncImpls.xDeleteNoWait(...args);
|
||||||
|
storeAndNotify('xDelete', rc);
|
||||||
|
},
|
||||||
|
mkdir: async function(dirname){
|
||||||
|
let rc = 0;
|
||||||
|
try {
|
||||||
|
await getDirForPath(dirname+"/filepart", true);
|
||||||
|
}catch(e){
|
||||||
|
//error("mkdir failed",filename, e.message);
|
||||||
|
rc = state.sq3Codes.SQLITE_IOERR;
|
||||||
|
}
|
||||||
|
storeAndNotify('mkdir', rc);
|
||||||
},
|
},
|
||||||
xFileSize: async function(fid){
|
xFileSize: async function(fid){
|
||||||
log("xFileSize(",arguments,")");
|
log("xFileSize(",arguments,")");
|
||||||
|
@ -15,8 +15,10 @@
|
|||||||
very much incomplete, under construction, and experimental.
|
very much incomplete, under construction, and experimental.
|
||||||
<strong>See the dev console for all output.</strong>
|
<strong>See the dev console for all output.</strong>
|
||||||
</div>
|
</div>
|
||||||
<div id='test-output'>
|
<div>
|
||||||
|
<a href='?delete'>Use this link</a> to delete the persistent OPFS-side db (if any).
|
||||||
</div>
|
</div>
|
||||||
|
<div id='test-output'></div>
|
||||||
<script>new Worker("test-opfs-vfs.js"+self.location.search);</script>
|
<script>new Worker("test-opfs-vfs.js"+self.location.search);</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -34,12 +34,16 @@ const tryOpfsVfs = function(sqlite3){
|
|||||||
const oVfs = capi.sqlite3_vfs.instanceForPointer(pVfs) || toss("Unexpected instanceForPointer() result.");;
|
const oVfs = capi.sqlite3_vfs.instanceForPointer(pVfs) || toss("Unexpected instanceForPointer() result.");;
|
||||||
log("OPFS VFS:",pVfs, oVfs);
|
log("OPFS VFS:",pVfs, oVfs);
|
||||||
|
|
||||||
|
const urlArgs = new URL(self.location.href).searchParams;
|
||||||
const dbFile = "my-persistent.db";
|
const dbFile = "my-persistent.db";
|
||||||
const db = new sqlite3.oo1.DB(dbFile, "c", "opfs");
|
if(urlArgs.has('delete')) sqlite3.opfs.deleteEntry(dbFile);
|
||||||
|
|
||||||
|
const opfs = sqlite3.opfs;
|
||||||
|
const db = new opfs.OpfsDb(dbFile);
|
||||||
log("db file:",db.filename);
|
log("db file:",db.filename);
|
||||||
try{
|
try{
|
||||||
let n = db.selectValue("select count(*) from sqlite_schema");
|
if(opfs.entryExists(dbFile)){
|
||||||
if(n){
|
let n = db.selectValue("select count(*) from sqlite_schema");
|
||||||
log("Persistent data found. sqlite_schema entry count =",n);
|
log("Persistent data found. sqlite_schema entry count =",n);
|
||||||
}
|
}
|
||||||
db.transaction((db)=>{
|
db.transaction((db)=>{
|
||||||
@ -54,6 +58,17 @@ const tryOpfsVfs = function(sqlite3){
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
log("count(*) from t =",db.selectValue("select count(*) from t"));
|
log("count(*) from t =",db.selectValue("select count(*) from t"));
|
||||||
|
|
||||||
|
// Some sanity checks of the opfs utility functions...
|
||||||
|
const testDir = '/sqlite3-opfs-'+opfs.randomFilename(12);
|
||||||
|
const aDir = testDir+'/test/dir';
|
||||||
|
opfs.mkdir(aDir) || toss("mkdir failed");
|
||||||
|
opfs.mkdir(aDir) || toss("mkdir must pass if the dir exists");
|
||||||
|
opfs.deleteEntry(testDir+'/test') && toss("delete 1 should have failed (dir not empty)");
|
||||||
|
opfs.deleteEntry(testDir+'/test/dir') || toss("delete 2 failed");
|
||||||
|
opfs.deleteEntry(testDir+'/test/dir') && toss("delete 2b should have failed (dir already deleted)");
|
||||||
|
opfs.deleteEntry(testDir,true) || toss("delete 3 failed");
|
||||||
|
opfs.entryExists(testDir) && toss("entryExists(",testDir,") should have failed");
|
||||||
}finally{
|
}finally{
|
||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
@ -62,10 +77,9 @@ const tryOpfsVfs = function(sqlite3){
|
|||||||
}/*tryOpfsVfs()*/;
|
}/*tryOpfsVfs()*/;
|
||||||
|
|
||||||
importScripts('sqlite3.js');
|
importScripts('sqlite3.js');
|
||||||
self.sqlite3InitModule().then((EmscriptenModule)=>{
|
self.sqlite3InitModule()
|
||||||
EmscriptenModule.sqlite3.installOpfsVfs()
|
.then((EmscriptenModule)=>EmscriptenModule.sqlite3.installOpfsVfs())
|
||||||
.then((sqlite3)=>tryOpfsVfs(sqlite3))
|
.then((sqlite3)=>tryOpfsVfs(sqlite3))
|
||||||
.catch((e)=>{
|
.catch((e)=>{
|
||||||
console.error("Error initializing OPFS VFS:",e);
|
console.error("Error initializing module:",e);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
@ -1057,7 +1057,7 @@
|
|||||||
|
|
||||||
let dbName = "/testing1.sqlite3";
|
let dbName = "/testing1.sqlite3";
|
||||||
let vfsName = undefined;
|
let vfsName = undefined;
|
||||||
if(capi.sqlite3_web_db_is_kvvfs()){
|
if(capi.sqlite3_web_db_uses_vfs(0,"kvvfs")){
|
||||||
dbName = "local";
|
dbName = "local";
|
||||||
vfsName = 'kvvfs';
|
vfsName = 'kvvfs';
|
||||||
logHtml("Found kvvfs. Clearing db(s) from sessionStorage and localStorage",
|
logHtml("Found kvvfs. Clearing db(s) from sessionStorage and localStorage",
|
||||||
@ -1065,9 +1065,9 @@
|
|||||||
clearKvvfs();
|
clearKvvfs();
|
||||||
}
|
}
|
||||||
const db = new oo.DB(dbName,'c',vfsName), startTime = performance.now();
|
const db = new oo.DB(dbName,'c',vfsName), startTime = performance.now();
|
||||||
log("capi.sqlite3_web_db_is_kvvfs() ==",capi.sqlite3_web_db_is_kvvfs(db.pointer));
|
log("db is kvvfs?",capi.sqlite3_web_db_uses_vfs(db.pointer,"kvvfs"));
|
||||||
try {
|
try {
|
||||||
log("db.filename =",db.filename,"db.fileName() =",db.fileName());
|
log("db.filename =",db.filename,"db.fileName() =",db.getFilename());
|
||||||
const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>',
|
const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>',
|
||||||
banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<';
|
banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<';
|
||||||
[
|
[
|
||||||
|
33
manifest
33
manifest
@ -1,5 +1,5 @@
|
|||||||
C Move\sthe\sOPFS\sVFS\sbits\sback\sinto\sapi/sqlite3-api-opfs.js.\sRefactor\sthe\sOPFS\sVFS\sinit\sprocess\sto\suse\sa\sPromise-returning\sfunction\swhich\sthe\sclient\smust\scall,\sas\sthat\seliminates\sany\suncertainty\sabout\swhen\sthe\sVFS\s(necessarily\sactivated\sasynchronously)\sactually\sbecomes\savailable\sto\sthe\sclient.\sRename\sx-sync-async.*\sto\stest-opfs-vfs.*\sMilestone:\sfirst\ssuccessful\stest\sof\sOPFS\swithout\sWASMFS.
|
C Numerous\scleanups\sin\sthe\sJS\sbits.\sRemoved\ssome\snow-defunct\swasm\stest\sfiles.\sExpose\ssqlite3.opfs\sobject\scontaining\svarious\sOPFS-specific\sutilities.
|
||||||
D 2022-09-18T03:05:55.278
|
D 2022-09-18T17:32:35.336
|
||||||
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
|
||||||
@ -483,10 +483,10 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814
|
|||||||
F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b
|
F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b
|
||||||
F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77
|
F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77
|
||||||
F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30
|
F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30
|
||||||
F ext/wasm/api/sqlite3-api-oo1.js d7526517f7ad3f6bda16ad66d373bbb71b43168deef7af60eda5c9fe873d1387
|
F ext/wasm/api/sqlite3-api-oo1.js 2d13dddf0d2b4168a9249f124134d37924331e5b55e05dba18b6d661fbeefe48
|
||||||
F ext/wasm/api/sqlite3-api-opfs.js 87d98f2449d5790efd7044e492166e4ed767e3320a03ed5a173b2b9364fc4896
|
F ext/wasm/api/sqlite3-api-opfs.js 4090abf4e16b460543ff665e96822048e37a2703e0ba46a01fed3a15c024c034
|
||||||
F ext/wasm/api/sqlite3-api-prologue.js 48ebca4ae340b0242d4f39bbded01bd0588393c8023628be1c454b4db6f7bd6e
|
F ext/wasm/api/sqlite3-api-prologue.js 4e3e26880d444000cca1b4f3ddfa9d49581dfecd1de9426080239ecc208c447d
|
||||||
F ext/wasm/api/sqlite3-api-worker1.js d33062afa045fd4be01ba4abc266801807472558b862b30056211b00c9c347b4
|
F ext/wasm/api/sqlite3-api-worker1.js e8456bd9b93eab297d065b25cb7a253835f606f9349383f2aa5c585e8d3b3aef
|
||||||
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
||||||
F ext/wasm/api/sqlite3-wasm.c 4130e2df9587f4e4c3afc04c3549d682c8a5c0cfe5b22819a0a86edb7f01b9bd
|
F ext/wasm/api/sqlite3-wasm.c 4130e2df9587f4e4c3afc04c3549d682c8a5c0cfe5b22819a0a86edb7f01b9bd
|
||||||
F ext/wasm/batch-runner-kvvfs.html ef3b2f553abad4f17a2a29ce6526023793a88e8597b7a24b8c7855a030b90a16
|
F ext/wasm/batch-runner-kvvfs.html ef3b2f553abad4f17a2a29ce6526023793a88e8597b7a24b8c7855a030b90a16
|
||||||
@ -502,7 +502,7 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695
|
|||||||
F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8
|
F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8
|
||||||
F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08
|
F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08
|
||||||
F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686
|
F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686
|
||||||
F ext/wasm/index.html 492eb6c9023c9cda391c61702a28bd18e6c986ca2588ec3f384275bb77275285
|
F ext/wasm/index.html d698cc021c25ca940f67805c2cc2848c303705d98b4c4f9f55565b9a9c37d2bb
|
||||||
F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215
|
F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215
|
||||||
F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106
|
F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106
|
||||||
F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f
|
F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f
|
||||||
@ -510,11 +510,8 @@ F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e5
|
|||||||
F ext/wasm/kvvfs.make 4b2ba6d061f3a52da9f5812f86f4faa80fb4d9456a152f6b0585dccd667a4e22
|
F ext/wasm/kvvfs.make 4b2ba6d061f3a52da9f5812f86f4faa80fb4d9456a152f6b0585dccd667a4e22
|
||||||
F ext/wasm/kvvfs1.html 13bb24190bfb276a57b228499519badcc1bf39ed07e4b37bc2a425ce6418fed1
|
F ext/wasm/kvvfs1.html 13bb24190bfb276a57b228499519badcc1bf39ed07e4b37bc2a425ce6418fed1
|
||||||
F ext/wasm/kvvfs1.js ec1c1d071bb055711f9151df05616111432cf3e6bf7ac7f8dcbcfb56c9d9ed48
|
F ext/wasm/kvvfs1.js ec1c1d071bb055711f9151df05616111432cf3e6bf7ac7f8dcbcfb56c9d9ed48
|
||||||
F ext/wasm/scratchpad-opfs-worker.html 5fdda167571264300f388847d34f00b77dd48984a8dba2ee9c099c3ffa05db66
|
|
||||||
F ext/wasm/scratchpad-opfs-worker.js cf6c4554d3b099c1a50013e50d19b3dc60e183511b4b4dbe7fabc2b9d3360567
|
|
||||||
F ext/wasm/scratchpad-opfs-worker2.js 8c980370bbd5a262d96af8627c443936e11b87d0263a02123769d5953fc146da
|
|
||||||
F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06
|
F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06
|
||||||
F ext/wasm/scratchpad-wasmfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e
|
F ext/wasm/scratchpad-wasmfs-main.js f0836e3576df7a89390d777bb53e142e559e8a79becfb2a5a976490b05a1c4fa
|
||||||
F ext/wasm/speedtest1-kvvfs.html c8b65c20e2b35298dc02d8e0a394d5e1eb857fd22e504468388234aee13aef08
|
F ext/wasm/speedtest1-kvvfs.html c8b65c20e2b35298dc02d8e0a394d5e1eb857fd22e504468388234aee13aef08
|
||||||
F ext/wasm/speedtest1-wasmfs.html 6a67a6812f03a2058eb5c6ad0c8dea4bf749d0160ed9d6b826dabe7b766c3cf7
|
F ext/wasm/speedtest1-wasmfs.html 6a67a6812f03a2058eb5c6ad0c8dea4bf749d0160ed9d6b826dabe7b766c3cf7
|
||||||
F ext/wasm/speedtest1-worker.html d8881ae802d15fb8adb94049265173e99f350e07e1d4e6f9e1cbd8969fe63a04
|
F ext/wasm/speedtest1-worker.html d8881ae802d15fb8adb94049265173e99f350e07e1d4e6f9e1cbd8969fe63a04
|
||||||
@ -523,15 +520,15 @@ F ext/wasm/speedtest1.html fbb8e4d1639028443f3687a683be660beca6927920545cf6b1fdf
|
|||||||
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
|
||||||
F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
|
F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
|
||||||
F ext/wasm/sqlite3-opfs-async-proxy.js 456bef1253fd4732f133b601a4450b7f8461e67af6e8d30bf8a239ad775c77a2
|
F ext/wasm/sqlite3-opfs-async-proxy.js 6e89e1af7c616afdd877cbcf5d0ec3d5f47ba252b9a19e696140b9495dc1e653
|
||||||
F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9
|
F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9
|
||||||
F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e
|
F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e
|
||||||
F ext/wasm/test-opfs-vfs.html 3e11c875c28f041891deeea6b2375121845ee1269cac6747df957ec0c7d4d37a w ext/wasm/x-sync-async.html
|
F ext/wasm/test-opfs-vfs.html eb69dda21eb414b8f5e3f7c1cc0f774103cc9c0f87b2d28a33419e778abfbab5
|
||||||
F ext/wasm/test-opfs-vfs.js bf70cd553a443b4eda63b577787ef73144f879fa062a20a73bb44e3242c81a15 w ext/wasm/x-sync-async.js
|
F ext/wasm/test-opfs-vfs.js 753c6b86dd8ce0813121add44726a038ba1b83acebdc8414189cb163faf23e6d
|
||||||
F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893
|
F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893
|
||||||
F ext/wasm/testing-worker1-promiser.js 63448fddfd3b8c89ff667d17c8b31c6c2259dd4647ebbbd28f3a921c48e924da
|
F ext/wasm/testing-worker1-promiser.js 63448fddfd3b8c89ff667d17c8b31c6c2259dd4647ebbbd28f3a921c48e924da
|
||||||
F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae
|
F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae
|
||||||
F ext/wasm/testing1.js 7cd8ab255c238b030d928755ae8e91e7d90a12f2ae601b1b8f7827aaa4fb258e
|
F ext/wasm/testing1.js 507001a970fe8a8eb67b6c8d783e1c1daa3db2719f727c4551af29349410e538
|
||||||
F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3
|
F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3
|
||||||
F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c
|
F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c
|
||||||
F ext/wasm/wasmfs.make 21a5cf297954a689e0dc2a95299ae158f681cae5e90c10b99d986097815fd42d
|
F ext/wasm/wasmfs.make 21a5cf297954a689e0dc2a95299ae158f681cae5e90c10b99d986097815fd42d
|
||||||
@ -2030,8 +2027,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 1c660970d0f62bcfd6e698a72b050d99972a1e39f45a5ac24194a190f8f78ab3
|
P b2abf60dbfa6648f671a3932cb65feb28d05a0d5b7f792351d14f9c13d9798c5
|
||||||
R 039a2cd7ec4da87358ad6184da91bc68
|
R 79bf69f52699f5d039971f9fe79e1ec3
|
||||||
U stephan
|
U stephan
|
||||||
Z 58e9ce410c47c756bc153984b857c4cf
|
Z 77273ebd9a43ebba5449759a950a89f3
|
||||||
# Remove this line to create a well-formed Fossil manifest.
|
# Remove this line to create a well-formed Fossil manifest.
|
||||||
|
@ -1 +1 @@
|
|||||||
b2abf60dbfa6648f671a3932cb65feb28d05a0d5b7f792351d14f9c13d9798c5
|
26e625d05d9820033b23536f18ad3ddc59ed712ad507d4b0c7fe88abd15d2be8
|
Reference in New Issue
Block a user