*/
state.sq3Codes = Object.create(null);
[
+ 'SQLITE_ACCESS_EXISTS',
+ 'SQLITE_ACCESS_READWRITE',
'SQLITE_ERROR',
'SQLITE_IOERR',
'SQLITE_IOERR_ACCESS',
*/
opfsUtil.entryExists = async function(fsEntryName){
try {
- const [dh, fn] = await opfsUtil.getDirForFilename(filename);
+ const [dh, fn] = await opfsUtil.getDirForFilename(fsEntryName);
await dh.getFileHandle(fn);
return true;
}catch(e){
}
});
- /**
- An Error subclass specifically for reporting DB-level errors and
- enabling clients to unambiguously identify such exceptions.
- The C-level APIs never throw, but some of the higher-level
- C-style APIs do and the object-oriented APIs use exceptions
- exclusively to report errors.
- */
- class SQLite3Error extends Error {
- /**
- Constructs this object with a message equal to all arguments
- concatenated with a space between each one. As a special case,
- if it's passed only a single integer argument, the string form
- of that argument is the result of
- sqlite3.capi.sqlite3_js_rc_str() or (if that returns falsy), a
- synthesized string which contains that integer.
- */
- constructor(...args){
- if(1===args.length && 'number'===typeof args[0] && args[0]===(args[0] | 0)){
- super((capi.sqlite3_js_rc_str && capi.sqlite3_js_rc_str(args[0]))
- || ("Unknown result code #"+args[0]));
- }else{
- super(args.join(' '));
- }
- this.name = 'SQLite3Error';
- }
- };
-
/**
The main sqlite3 binding API gets installed into this object,
mimicking the C API as closely as we can. The numerous members
*/
const wasm = Object.create(null);
+ /** Internal helper for SQLite3Error ctor. */
+ const __rcStr = (rc)=>{
+ return (capi.sqlite3_js_rc_str && capi.sqlite3_js_rc_str(rc))
+ || ("Unknown result code #"+rc);
+ };
+
+ /** Internal helper for SQLite3Error ctor. */
+ const __isInt = (n)=>'number'===typeof n && n===(n | 0);
+
+ /**
+ An Error subclass specifically for reporting DB-level errors and
+ enabling clients to unambiguously identify such exceptions.
+ The C-level APIs never throw, but some of the higher-level
+ C-style APIs do and the object-oriented APIs use exceptions
+ exclusively to report errors.
+ */
+ class SQLite3Error extends Error {
+ /**
+ Constructs this object with a message depending on its arguments:
+
+ - If it's passed only a single integer argument, it is assumed
+ to be an sqlite3 C API result code. The message becomes the
+ result of sqlite3.capi.sqlite3_js_rc_str() or (if that returns
+ falsy) a synthesized string which contains that integer.
+
+ - If passed 2 arguments and the 2nd is a object, it bevaves
+ like the Error(string,object) constructor except that the first
+ argument is subject to the is-integer semantics from the
+ previous point.
+
+ - Else all arguments are concatenated with a space between each
+ one, using args.join(' '), to create the error message.
+ */
+ constructor(...args){
+ if(1===args.length && __isInt(args[0])){
+ super(__rcStr(args[0]));
+ }else if(2===args.length && 'object'===typeof args){
+ if(__isInt(args[0])) super(__rcStr(args[0]), args[1]);
+ else super(...args);
+ }else{
+ super(args.join(' '));
+ }
+ this.name = 'SQLite3Error';
+ }
+ };
+
/**
Functionally equivalent to the SQLite3Error constructor but may
be used as part of an expression, e.g.:
wasm.bindingSignatures.wasm = [
["sqlite3_wasm_db_reset", "int", "sqlite3*"],
["sqlite3_wasm_db_vfs", "sqlite3_vfs*", "sqlite3*","string"],
+ ["sqlite3_wasm_vfs_create_file", "int",
+ "sqlite3_vfs*","string","*", "int"],
["sqlite3_wasm_vfs_unlink", "int", "sqlite3_vfs*","string"]
];
return wState.dbList[0] && getDbId(wState.dbList[0]);
};
+ const guessVfs = function(filename){
+ const m = /^file:.+(vfs=(\w+))/.exec(filename);
+ return sqlite3.capi.sqlite3_vfs_find(m ? m[2] : 0);
+ };
+
+ const isSpecialDbFilename = (n)=>{
+ return ''===n || ':'===n[0];
+ };
+
/**
A level of "organizational abstraction" for the Worker1
API. Each method in this object must map directly to a Worker1
}
const rc = Object.create(null);
const pDir = sqlite3.capi.sqlite3_wasmfs_opfs_dir();
- if(!args.filename || ':memory:'===args.filename){
+ let byteArray, pVfs;
+ oargs.vfs = args.vfs;
+ if(isSpecialDbFilename(args.filename)){
oargs.filename = args.filename || '';
}else{
oargs.filename = args.filename;
- oargs.vfs = args.vfs;
+ byteArray = args.byteArray;
+ if(byteArray) pVfs = guessVfs(args.filename);
+ }
+ if(pVfs){
+ /* 2022-11-02: this feature is as-yet untested except that
+ sqlite3_wasm_vfs_create_file() has been tested from the
+ browser dev console. */
+ let pMem;
+ try{
+ pMem = sqlite3.wasm.allocFromTypedArray(byteArray);
+ const rc = sqlite3.wasm.sqlite3_wasm_vfs_create_file(
+ pVfs, oargs.filename, pMem, byteArray.byteLength
+ );
+ if(rc) sqlite3.SQLite3Error.toss(rc);
+ }catch(e){
+ throw new sqlite3.SQLite3Error(
+ e.name+' creating '+args.filename+": "+e.message, {
+ cause: e
+ }
+ );
+ }finally{
+ if(pMem) sqlite3.wasm.dealloc(pMem);
+ }
}
const db = wState.open(oargs);
rc.filename = db.filename;
filename: db && db.filename
};
if(db){
- // Keep the "unlink" flag undocumented until we figure out how
- // to apply it consistently, independent of the db storage.
- wState.close(db, ((ev.args && 'object'===typeof ev.args)
- ? !!ev.args.unlink : false));
+ const doUnlink = ((ev.args && 'object'===typeof ev.args)
+ ? !!ev.args.unlink : false);
+ wState.close(db, doUnlink);
}
return response;
},
sqlite3_serialize(). Response is an object:
{
- bytearray: Uint8Array (db file contents),
+ byteArray: Uint8Array (db file contents),
filename: the current db filename,
mimetype: 'application/x-sqlite3'
}
export: function(ev){
const db = getMsgDb(ev);
const response = {
- bytearray: sqlite3.capi.sqlite3_js_db_export(db.pointer),
+ byteArray: sqlite3.capi.sqlite3_js_db_export(db.pointer),
filename: db.filename,
mimetype: 'application/x-sqlite3'
};
- wState.xfer.push(response.bytearray.buffer);
+ wState.xfer.push(response.byteArray.buffer);
return response;
}/*export()*/,
const affirmNotRO = function(opName,fh){
if(fh.readOnly) toss(opName+"(): File is read-only: "+fh.filenameAbs);
};
+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
+ lock (that seems like a bug but it's currently uninvestigated).
+ This means, however, that some OPFS VFS routines may trigger
+ 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
let rc;
wTimeStart('xFileSize');
try{
+ affirmLocked('xFileSize',fh);
rc = await (await getSyncHandle(fh)).getSize();
state.s11n.serialize(Number(rc));
rc = 0;
let rc = 0, nRead;
const fh = __openFiles[fid];
try{
+ affirmLocked('xRead',fh);
wTimeStart('xRead');
nRead = (await getSyncHandle(fh)).read(
fh.sabView.subarray(0, n),
const fh = __openFiles[fid];
wTimeStart('xTruncate');
try{
+ affirmLocked('xTruncate',fh);
affirmNotRO('xTruncate', fh);
await (await getSyncHandle(fh)).truncate(size);
}catch(e){
xWrite: async function(fid/*sqlite3_file pointer*/,n,offset64){
mTimeStart('xWrite');
let rc;
+ const fh = __openFiles[fid];
wTimeStart('xWrite');
try{
- const fh = __openFiles[fid];
+ affirmLocked('xWrite',fh);
affirmNotRO('xWrite', fh);
rc = (
n === (await getSyncHandle(fh))
**
** emcc -o sqlite3.wasm ... -I/path/to/sqlite3-c-and-h sqlite3-wasm.c
*/
-
#define SQLITE_WASM
#ifdef SQLITE_WASM_ENABLE_C_TESTS
/*
** sqlite3_free() to free it.
*/
SQLITE_WASM_KEEP
-int sqlite3_wasm_db_serialize( sqlite3* pDb, unsigned char **pOut,
- sqlite3_int64 * nOut, unsigned int mFlags ){
+int sqlite3_wasm_db_serialize( sqlite3 *pDb, unsigned char **pOut,
+ sqlite3_int64 *nOut, unsigned int mFlags ){
unsigned char * z;
if( !pDb || !pOut ) return SQLITE_MISUSE;
if(nOut) *nOut = 0;
}
}
+/*
+** This function is NOT part of the sqlite3 public API. It is strictly
+** for use by the sqlite project's own JS/WASM bindings.
+**
+** Creates a new file using the I/O API of the given VFS, containing
+** the given number of bytes of the given data. If the file exists,
+** it is truncated to the given length and populated with the given
+** data.
+**
+** This function exists so that we can implement the equivalent of
+** Emscripten's FS.createDataFile() in a VFS-agnostic way. This
+** functionality is intended for use in uploading database files.
+**
+** If pVfs is NULL, sqlite3_vfs_find(0) is used.
+**
+** If zFile is NULL, pVfs is NULL (and sqlite3_vfs_find(0) returns
+** NULL), or nData is negative, SQLITE_MISUSE are returned.
+**
+** On success, it creates a new file with the given name, populated
+** with the fist nData bytes of pData. If pData is NULL, the file is
+** created and/or truncated to nData bytes.
+**
+** Whether or not directory components of zFilename are created
+** automatically or not is unspecified: that detail is left to the
+** VFS. The "opfs" VFS, for example, create them.
+**
+** Not all VFSes support this functionality, e.g. the "kvvfs" does
+** not.
+**
+** If an error happens while populating or truncating the file, the
+** target file will be deleted (if needed) if this function created
+** it. If this function did not create it, it is not deleted but may
+** be left in an undefined state.
+**
+** Returns 0 on success. On error, it returns a code described above
+** or propagates a code from one of the I/O methods.
+**
+** Design note: nData is an integer, instead of int64, for WASM
+** portability, so that the API can still work in builds where BigInt
+** support is disabled or unavailable.
+*/
+SQLITE_WASM_KEEP
+int sqlite3_wasm_vfs_create_file( sqlite3_vfs *pVfs,
+ const char *zFilename,
+ const unsigned char * pData,
+ int nData ){
+ int rc;
+ sqlite3_file *pFile = 0;
+ sqlite3_io_methods const *pIo;
+ const int openFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
+ int flagsOut = 0;
+ int fileExisted = 0;
+ int doUnlock = 0;
+ const unsigned char *pPos = pData;
+ const int blockSize = 512
+ /* Because we are using pFile->pMethods->xWrite() for writing, and
+ ** it may have a buffer limit related to sqlite3's pager size, we
+ ** conservatively write in 512-byte blocks (smallest page
+ ** size). */;
+
+ if( !pVfs ) pVfs = sqlite3_vfs_find(0);
+ if( !pVfs || !zFilename || nData<0 ) return SQLITE_MISUSE;
+ pVfs->xAccess(pVfs, zFilename, SQLITE_ACCESS_EXISTS, &fileExisted);
+ rc = sqlite3OsOpenMalloc(pVfs, zFilename, &pFile, openFlags, &flagsOut);
+ if(rc) return rc;
+ pIo = pFile->pMethods;
+ if( pIo->xLock ) {
+ /* We need xLock() in order to accommodate the OPFS VFS, as it
+ ** obtains a writeable handle via the lock operation and releases
+ ** it in xUnlock(). If we don't do those here, we have to add code
+ ** to the VFS to account check whether it was locked before
+ ** xFileSize(), xTruncate(), and the like, and release the lock
+ ** only if it was unlocked when the op was started. */
+ rc = pIo->xLock(pFile, SQLITE_LOCK_EXCLUSIVE);
+ doUnlock = 0==rc;
+ }
+ if( 0==rc) rc = pIo->xTruncate(pFile, nData);
+ if( 0==rc && 0!=pData && nData>0 ){
+ while( 0==rc && nData>0 ){
+ const int n = nData>=blockSize ? blockSize : nData;
+ rc = pIo->xWrite(pFile, pPos, n, (sqlite3_int64)(pPos - pData));
+ nData -= n;
+ pPos += n;
+ }
+ if( 0==rc && nData>0 ){
+ assert(nData<512);
+ rc = pIo->xWrite(pFile, pPos, nData, (sqlite3_int64)(pPos - pData));
+ }
+ }
+ if( pIo->xUnlock && doUnlock!=0 ) pIo->xUnlock(pFile, SQLITE_LOCK_NONE);
+ pIo->xClose(pFile);
+ if( rc!=0 && 0==fileExisted ){
+ pVfs->xDelete(pVfs, zFilename, 1);
+ }
+ return rc;
+}
+
/*
** This function is NOT part of the sqlite3 public API. It is strictly
** for use by the sqlite project's own JS/WASM bindings.
.assert(2===ev.resultRows[0][0]);
});
+ await wtest('export', function(ev){
+ ev = ev.result;
+ T.assert('string' === typeof ev.filename)
+ .assert(ev.byteArray instanceof Uint8Array)
+ .assert(ev.byteArray.length > 1024)
+ .assert('application/x-sqlite3' === ev.mimetype);
+ });
+
/***** close() tests must come last. *****/
await wtest('close',{},function(ev){
T.assert('string' === typeof ev.result.filename);
T.assert(1===ev.resultRows.length)
.assert(2===ev.resultRows[0][0]);
});
- if(0){
- // export requires reimpl. for portability reasons.
- runOneTest('export',{}, function(ev){
- ev = ev.result;
- T.assert('string' === typeof ev.filename)
- .assert(ev.buffer instanceof Uint8Array)
- .assert(ev.buffer.length > 1024)
- .assert('application/x-sqlite3' === ev.mimetype);
- });
- }
+ runOneTest('export',{}, function(ev){
+ ev = ev.result;
+ log("export result:",ev);
+ T.assert('string' === typeof ev.filename)
+ .assert(ev.byteArray instanceof Uint8Array)
+ .assert(ev.byteArray.length > 1024)
+ .assert('application/x-sqlite3' === ev.mimetype);
+ });
/***** close() tests must come last. *****/
runOneTest('close',{unlink:true},function(ev){
ev = ev.result;
}
};
log("Init complete, but async init bits may still be running.");
+ log("Installing Worker into global scope SW for dev purposes.");
+ self.SW = SW;
})();
}
try{ throw new sqlite3.SQLite3Error(capi.SQLITE_SCHEMA) }
catch(e){ T.assert('SQLITE_SCHEMA' === e.message) }
+ try{ sqlite3.SQLite3Error.toss(capi.SQLITE_CORRUPT,{cause: true}) }
+ catch(e){
+ T.assert('SQLITE_CORRUPT'===e.message)
+ .assert(true===e.cause);
+ }
})
////////////////////////////////////////////////////////////////////
.t('strglob/strlike', function(sqlite3){
unlink();
}
+ if(1){
+ // Sanity-test sqlite3_wasm_vfs_create_file()...
+ const fSize = 1379;
+ let sh;
+ try{
+ T.assert(!(await opfs.entryExists(filename)));
+ let rc = wasm.sqlite3_wasm_vfs_create_file(
+ pVfs, filename, null, fSize
+ );
+ T.assert(0===rc)
+ .assert(await opfs.entryExists(filename));
+ const fh = await opfs.rootDirectory.getFileHandle(filename);
+ sh = await fh.createSyncAccessHandle();
+ T.assert(fSize === await sh.getSize());
+ }finally{
+ if(sh) sh.close();
+ unlink();
+ }
+ }
+
// Some sanity checks of the opfs utility functions...
const testDir = '/sqlite3-opfs-'+opfs.randomFilename(12);
const aDir = testDir+'/test/dir';
-C Emcc\sseems\sconfused\sby\sSQLITE_DEBUG,\sfor\sreasons\sunknown.\s\sUse\sNDEBUG\sinstead\nto\ssimplify\sthe\s#ifdef\slogic\sin\ssqlite3recover.c.
-D 2022-11-02T11:25:33.199
+C Add\ssqlite3_wasm_vfs_create_file()\sto\sreplace\sEmscripten's\sFS.createDataFile()\sin\sa\s(mostly)\sVFS-agnostic\sway.\sAdd\sa\stest\sfor\sworker1's\sexport\s(to\sbytearray)\ssupport.\sRe-add\sworker1\sopen-from-bytearray\susing\ssqlite3_wasm_vfs_create_file()\sbut\sit's\suntested\s(requires\sa\snew\sinteractive\stest\sapp\sor\smaybe\sreconsideration).
+D 2022-11-02T11:53:31.000
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/wasm/api/sqlite3-api-cleanup.js ecdc69dbfccfe26146f04799fcfd4a6f5790d46e7e3b9b6e9b0491f92ed8ae34
F ext/wasm/api/sqlite3-api-glue.js 9cfa26a9818532c80c2555bc98615de3b170d5db0cf4b141cc3aa83c33c8758f
F ext/wasm/api/sqlite3-api-oo1.js e9a83489bbb4838ce0aee46eaaa9350e0e25a5b926b565e4f5ae8e840e4fbaed
-F ext/wasm/api/sqlite3-api-opfs.js 59b278ed00764fc47ba88be0582ab3fc3ce725e02b6d86459464cc029b9ac356
-F ext/wasm/api/sqlite3-api-prologue.js 201b9ab9d101fdefa3175b02748ef39cba43bc7abe87a69b6faac24e58cd70f0
-F ext/wasm/api/sqlite3-api-worker1.js 4f920a54fb97d4ca50632d45bd7d011a55016eb5a5883725033abb450903bc6f
+F ext/wasm/api/sqlite3-api-opfs.js cdcbb57acc66f4569ac9e18f9d13d5a3657d8aae195725c6324943da56c1005d
+F ext/wasm/api/sqlite3-api-prologue.js 1f97261b4d8a60a48f30ee41261e1c4a0c3efff35ae08d7ece243fcf49b3eee3
+F ext/wasm/api/sqlite3-api-worker1.js cac2f5c63f950f69b5249c9880d4cd385e914c354c459d4096ed5dbb1248de76
F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3
-F ext/wasm/api/sqlite3-opfs-async-proxy.js 9815bd045b638af06350bfe04eaaae17ddf33c3593a9ca2d3f7fdbfd3c6a2205
+F ext/wasm/api/sqlite3-opfs-async-proxy.js 936f57737eb65afc0f4c3494b93f7b02208055226a7b3cb58f551c38b03ab083
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
-F ext/wasm/api/sqlite3-wasm.c 14ac9c03f6585332f882703f3427f11ffe8ffe8b6c0e252be2c518f7aac6ab6a
+F ext/wasm/api/sqlite3-wasm.c 41f4c807d5e027d5dd61d507cb0a78d0cee5618220e100860ff4c45ed1bd7c78
F ext/wasm/api/sqlite3-worker1-promiser.js 0c7a9826dbf82a5ed4e4f7bf7816e825a52aff253afbf3350431f5773faf0e4b
F ext/wasm/api/sqlite3-worker1.js 1e54ea3d540161bcfb2100368a2fc0cad871a207b8336afee1c445715851ec54
F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8
F ext/wasm/demo-jsstorage.html 409c4be4af5f207fb2877160724b91b33ea36a3cd8c204e8da1acb828ffe588e
F ext/wasm/demo-jsstorage.js 44e3ae7ec2483b6c511384c3c290beb6f305c721186bcf5398ca4e00004a06b8
F ext/wasm/demo-worker1-promiser.html 1de7c248c7c2cfd4a5783d2aa154bce62d74c6de98ab22f5786620b3354ed15f
-F ext/wasm/demo-worker1-promiser.js 988ce92220c1cf1dd95fcb6b59734f0ae677942469390ddd8a64f4bbb5f99821
+F ext/wasm/demo-worker1-promiser.js b85a2bb1b918db4f09dfa24419241cb3edad7791389425c2505092e9b715017d
F ext/wasm/demo-worker1.html 2c178c1890a2beb5a5fecb1453e796d067a4b8d3d2a04d65ca2eb1ab2c68ef5d
-F ext/wasm/demo-worker1.js 117e4eedc62e103e287f0e4a694add7e13a200a4d7056e718645032288c4a8ab
+F ext/wasm/demo-worker1.js a619adffc98b75b66c633b00f747b856449a134a9a0357909287d80a182d70fa
F ext/wasm/dist.make 481289899a07958439d07ee4302ff86235fa0fbb72f17ea05db2be90a94abf90
F ext/wasm/fiddle.make e570ec1bfc7d803507a2e514fe32f673fe001b2114b85c73c3964a462ba8bcfc
F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
F ext/wasm/test-opfs-vfs.js 44363db07b2a20e73b0eb1808de4400ca71b703af718d0fa6d962f15e73bf2ac
F ext/wasm/tester1-worker.html 51bf39e2b87f974ae3d5bc3086e2fb36d258f3698c54f6e21ba4b3b99636fa27
F ext/wasm/tester1.html 624ec41cd9f78a1f2b6d7df70aaa7a6394396b1f2455ecbd6de5775c1275b121
-F ext/wasm/tester1.js 157eb499aad3365e33a7d95d6847c1d58d533335bc19d79bd3bc700b6350d1a5
+F ext/wasm/tester1.js db50ca105d683d1089f540dae4c2baf89a3c101704a05f22cefdcb517587ae8c
F ext/wasm/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd72273503ae7d5
F ext/wasm/wasmfs.make fb2d3c4a298b12cf1ec994ad1d0f1d027ae297449b364cde43d2eb807d68048f
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 5bc83d569594e104e90b1acef1a5fd23655b2089de393a6776e799fdef2082f5
-R 9c17a1415bba7769c29ae72a7a64647a
-U drh
-Z c15361b7db363b8dd30434361c36431b
+P 2610779ac84ac4a1a6901b6244653faf0c49ac6f0a4710a19aaf2a13106ae742
+R 54447cdf301faadeadecf9cb178cd6b7
+U stephan
+Z 0f92ea26c92634fd686db2350d9751fe
# Remove this line to create a well-formed Fossil manifest.
-2610779ac84ac4a1a6901b6244653faf0c49ac6f0a4710a19aaf2a13106ae742
\ No newline at end of file
+b35e1225c91a3cadc0d25af1e4e790237256d194990faa13190e343ed03e11c5
\ No newline at end of file