1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Expand "sqlite3_vfs*" JS-to-WASM function argument conversions to accept VFS names (JS strings) and capi.sqlite3_vfs instances. Implement sqlite3_js_vfs_create_file() to facilitate creation of file-upload features which store the file in VFS-specific storage (where possible, e.g. "unix" and "opfs" VFSes). Correct an argument type check in the SQLite3Error and WasmAllocError constructors.

FossilOrigin-Name: e1009b16d351b23676ad7bffab0c91b373a92132eb855c9af61991b50cd237ed
This commit is contained in:
stephan
2022-12-01 03:55:28 +00:00
parent 85ec20ac66
commit 2b2199570d
6 changed files with 225 additions and 64 deletions

View File

@ -73,8 +73,27 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
('sqlite3_stmt*', aPtr)
('sqlite3_context*', aPtr)
('sqlite3_value*', aPtr)
('sqlite3_vfs*', aPtr)
('void*', aPtr);
('void*', aPtr)
/**
`sqlite3_vfs*`:
- v is-a string: use the result of sqlite3_vfs_find(v) but
throw if it returns 0.
- v is-a capi.sqlite3_vfs: use v.pointer.
- Else return the same as the `'*'` argument conversion.
*/
('sqlite3_vfs*', (v)=>{
if('string'===typeof v){
const x = capi.sqlite3_vfs_find(v);
/* A NULL sqlite3_vfs pointer will be treated as the default
VFS in many contexts. We specifically do not want that
behavior here. */
if(!x) sqlite3.SQLite3Error.toss("Unknown sqlite3_vfs name:",v);
return x;
}else if(v instanceof sqlite3.capi.sqlite3_vfs) v = v.pointer;
return aPtr(v);
});
wasm.xWrap.resultAdapter('sqlite3*', aPtr)
('sqlite3_context*', aPtr)
('sqlite3_stmt*', aPtr)
@ -588,8 +607,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
'version'
]){
for(const e of Object.entries(wasm.ctype[t])){
// ^^^ [k,v] there triggers a buggy code transormation via one
// of the Emscripten-driven optimizers.
// ^^^ [k,v] there triggers a buggy code transformation via
// one of the Emscripten-driven optimizers.
capi[e[0]] = e[1];
}
}

View File

@ -185,7 +185,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
constructor(...args){
if(1===args.length && __isInt(args[0])){
super(__rcStr(args[0]));
}else if(2===args.length && 'object'===typeof args){
}else if(2===args.length && 'object'===typeof args[1]){
if(__isInt(args[0])) super(__rcStr(args[0]), args[1]);
else super(...args);
}else{
@ -354,7 +354,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
message.
*/
constructor(...args){
if(2===args.length && 'object'===typeof args){
if(2===args.length && 'object'===typeof args[1]){
super(...args);
}else if(args.length){
super(args.join(' '));
@ -748,7 +748,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
/**
Reports info about compile-time options using
sqlite_compileoption_get() and sqlite3_compileoption_used(). It
sqlite3_compileoption_get() and sqlite3_compileoption_used(). It
has several distinct uses:
If optName is an array then it is expected to be a list of
@ -973,7 +973,6 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
["sqlite3_wasm_vfs_unlink", "int", "sqlite3_vfs*","string"]
];
/**
sqlite3.wasm.pstack (pseudo-stack) holds a special-case
stack-style allocator intended only for use with _small_ data of
@ -1328,6 +1327,76 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
: 0);
};
/**
Creates a file using the storage appropriate for the given
sqlite3_vfs. The first argument may be a VFS name (JS string
only, NOT a WASM C-string), WASM-managed `sqlite3_vfs*`, or
a capi.sqlite3_vfs instance. Pass 0 (a NULL pointer) to use the
default VFS. If passed a string which does not resolve using
sqlite3_vfs_find(), an exception is thrown. (Note that a WASM
C-string is not accepted because it is impossible to
distinguish from a C-level `sqlite3_vfs*`.)
The second argument, the filename, must be a JS or WASM C-string.
The 3rd may either be falsy, a valid WASM memory pointer, or a
Uint8Array. The 4th must be the length, in bytes, of the data
array to copy. If the 3rd argument is a Uint8Array and the 4th is
not a positive integer then the 4th defaults to the array's
byteLength value.
If data is falsy then a file is created with dataLen bytes filled
with uninitialized data (whatever truncate() leaves there). If
data is not falsy then a file is created or truncated and it is
filled with the first dataLen bytes of the data source.
Throws if any arguments are invalid or if creating or writing to
the file fails.
Note that most VFSes do _not_ automatically create directory
parts of filenames, nor do all VFSes have a concept of
directories. If the given filename is not valid for the given
VFS, an exception will be thrown. This function exists primarily
to assist in implementing file-upload capability, with the caveat
that clients must have some idea of the VFS into which they want
to upload and that VFS must support the operation.
VFS-specific notes:
- "memdb" and "kvvfs": results are undefined.
- "unix" and related: will use the WASM build's equivalent of the
POSIX I/O APIs.
- "opfs": if available, uses OPFS storage and _does_ create
directory parts of the filename.
*/
capi.sqlite3_js_vfs_create_file = function(vfs, filename, data, dataLen){
let pData;
if(!data) pData = 0;
else if(wasm.isPtr(data)){
pData = data;
}else if(data instanceof Uint8Array){
pData = wasm.allocFromTypedArray(data);
if(arguments.length<4 || !util.isInt32(dataLen) || dataLen<0){
dataLen = data.byteLength;
}
}else{
SQLite3Error.toss("Invalid 3rd argument type for sqlite3_js_vfs_create_file().");
}
if(!util.isInt32(dataLen) || dataLen<0){
wasm.dealloc(pData);
SQLite3Error.toss("Invalid 4th argument for sqlite3_js_vfs_create_file().");
}
try{
const rc = wasm.sqlite3_wasm_vfs_create_file(vfs, filename, pData, dataLen);
if(rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code",
capi.sqlite3_js_rc_str(rc));
}finally{
wasm.dealloc(pData);
}
};
if( util.isUIThread() ){
/* Features specific to the main window thread... */

View File

@ -1442,7 +1442,7 @@ self.WhWasmUtilInstaller = function(target){
exception.
Clients may map their own result and argument adapters using
xWrap.resultAdapter() and xWrap.argAdaptor(), noting that not all
xWrap.resultAdapter() and xWrap.argAdapter(), noting that not all
type conversions are valid for both arguments _and_ result types
as they often have different memory ownership requirements.
@ -1497,7 +1497,7 @@ self.WhWasmUtilInstaller = function(target){
};
}/*xWrap()*/;
/** Internal impl for xWrap.resultAdapter() and argAdaptor(). */
/** Internal impl for xWrap.resultAdapter() and argAdapter(). */
const __xAdapter = function(func, argc, typeName, adapter, modeName, xcvPart){
if('string'===typeof typeName){
if(1===argc) return xcvPart[typeName];
@ -1575,7 +1575,7 @@ self.WhWasmUtilInstaller = function(target){
*/
target.xWrap.argAdapter = function f(typeName, adapter){
return __xAdapter(f, arguments.length, typeName, adapter,
'argAdaptor()', xcv.arg);
'argAdapter()', xcv.arg);
};
/**
@ -1601,6 +1601,33 @@ self.WhWasmUtilInstaller = function(target){
return target.xWrap(fname, resultType, argTypes||[]).apply(null, args||[]);
};
/**
This function is ONLY exposed in the public API to facilitate
testing. It should not be used in application-level code, only
in test code.
Expects to be given (typeName, value) and returns a conversion
of that value as has been registered using argAdapter().
It throws if no adapter is found.
ACHTUNG: the adapter may require that a scopedAllocPush() is
active and it may allocate memory within that scope.
*/
target.xWrap.testConvertArg = cache.xWrap.convertArg;
/**
This function is ONLY exposed in the public API to facilitate
testing. It should not be used in application-level code, only
in test code.
Expects to be given (typeName, value) and returns a conversion
of that value as has been registered using resultAdapter().
It throws if no adapter is found.
ACHTUNG: the adapter may allocate memory which the caller may need
to know how to free.
*/
target.xWrap.testConvertResult = cache.xWrap.convertResult;
return target;
};

View File

@ -1236,8 +1236,19 @@ self.sqlite3InitModule = sqlite3InitModule;
//log("vfsList =",vfsList);
for(const v of vfsList){
T.assert('string' === typeof v)
.assert(capi.sqlite3_vfs_find(v) > 0);
.assert(wasm.isPtr(
capi.sqlite3_vfs_find(v)
));
}
// While we have vfsList handy, let's verify...
wasm.scopedAllocCall(()=>{
const vfsArg = (v)=>wasm.xWrap.testConvertArg('sqlite3_vfs*',v);
T.assert(wasm.isPtr(vfsArg(vfsList[0])));
const pVfs = capi.sqlite3_vfs_find(vfsList[0]);
const vfs = new capi.sqlite3_vfs(pVfs);
T.assert(wasm.isPtr(vfsArg(vfs)));
vfs.dispose();
});
}
/**
Trivia: the magic db name ":memory:" does not actually use the
@ -1784,14 +1795,15 @@ self.sqlite3InitModule = sqlite3InitModule;
////////////////////////////////////////////////////////////////////////
T.g('OPFS (Worker thread only and only in supported browsers)',
(sqlite3)=>{return !!sqlite3.opfs})
(sqlite3)=>!!sqlite3.opfs)
.t({
name: 'OPFS sanity checks',
name: 'OPFS db sanity checks',
test: async function(sqlite3){
const filename = 'sqlite3-tester1.db';
const pVfs = capi.sqlite3_vfs_find('opfs');
const filename = this.opfsDbFile = 'sqlite3-tester1.db';
const pVfs = this.opfsVfs = capi.sqlite3_vfs_find('opfs');
T.assert(pVfs);
const unlink = (fn=filename)=>wasm.sqlite3_wasm_vfs_unlink(pVfs,fn);
const unlink = this.opfsUnlink =
(fn=filename)=>{wasm.sqlite3_wasm_vfs_unlink(pVfs,fn)};
unlink();
let db = new sqlite3.oo1.OpfsDb(filename);
try {
@ -1808,22 +1820,57 @@ self.sqlite3InitModule = sqlite3InitModule;
db.close();
unlink();
}
if(sqlite3.opfs){
// Sanity-test sqlite3_wasm_vfs_create_file()...
}
}/*OPFS db sanity checks*/)
.t({
name: 'OPFS utility APIs and sqlite3_js_vfs_create_file()',
test: async function(sqlite3){
const filename = this.opfsDbFile;
const pVfs = this.opfsVfs;
const unlink = this.opfsUnlink;
T.assert(filename && pVfs && !!unlink);
unlink();
// Sanity-test sqlite3_js_vfs_create_file()...
/**************************************************************
ATTENTION CLIENT-SIDE USERS: sqlite3.opfs is NOT intended
for client-side use. It is only for this project's own
internal use. Its APIs are subject to change or removal at
any time.
***************************************************************/
const opfs = sqlite3.opfs;
const fSize = 1379;
let sh;
try{
T.assert(!(await opfs.entryExists(filename)));
let rc = wasm.sqlite3_wasm_vfs_create_file(
capi.sqlite3_js_vfs_create_file(
pVfs, filename, null, fSize
);
T.assert(0===rc)
.assert(await opfs.entryExists(filename));
const fh = await opfs.rootDirectory.getFileHandle(filename);
T.assert(await opfs.entryExists(filename));
let fh = await opfs.rootDirectory.getFileHandle(filename);
sh = await fh.createSyncAccessHandle();
T.assert(fSize === await sh.getSize());
await sh.close();
sh = undefined;
unlink();
T.assert(!(await opfs.entryExists(filename)));
const ba = new Uint8Array([1,2,3,4,5]);
capi.sqlite3_js_vfs_create_file(
"opfs", filename, ba
);
T.assert(await opfs.entryExists(filename));
fh = await opfs.rootDirectory.getFileHandle(filename);
sh = await fh.createSyncAccessHandle();
T.assert(ba.byteLength === await sh.getSize());
await sh.close();
sh = undefined;
unlink();
T.mustThrowMatching(()=>{
capi.sqlite3_js_vfs_create_file(
"no-such-vfs", filename, ba
);
}, "Unknown sqlite3_vfs name: no-such-vfs");
}finally{
if(sh) await sh.close();
unlink();
@ -1842,8 +1889,7 @@ self.sqlite3InitModule = sqlite3InitModule;
.assert(!(await opfs.entryExists(testDir)),
"entryExists(",testDir,") should have failed");
}
}
}/*OPFS sanity checks*/)
}/*OPFS util sanity checks*/)
;/* end OPFS tests */
////////////////////////////////////////////////////////////////////////

View File

@ -1,5 +1,5 @@
C Add\sa\stestcase()\smacro\sto\sverify\sthat\sthe\scase\sof\sa\sNOT\sNULL\serror\smessage\nhitting\sthe\sstring\slength\slimit.
D 2022-11-30T21:18:23.655
C Expand\s"sqlite3_vfs*"\sJS-to-WASM\sfunction\sargument\sconversions\sto\saccept\sVFS\snames\s(JS\sstrings)\sand\scapi.sqlite3_vfs\sinstances.\sImplement\ssqlite3_js_vfs_create_file()\sto\sfacilitate\screation\sof\sfile-upload\sfeatures\swhich\sstore\sthe\sfile\sin\sVFS-specific\sstorage\s(where\spossible,\se.g.\s"unix"\sand\s"opfs"\sVFSes).\sCorrect\san\sargument\stype\scheck\sin\sthe\sSQLite3Error\sand\sWasmAllocError\sconstructors.
D 2022-12-01T03:55:28.175
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -503,9 +503,9 @@ F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08
F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62
F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f
F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4
F ext/wasm/api/sqlite3-api-glue.js 03c40b65530d67bb2748b7380aea5fd1534500f812b76a6b401066dcd7fc4116
F ext/wasm/api/sqlite3-api-glue.js 5468ba750e081c55ff3c572e3ae47dd415be73907ce40117fb121f37f4d0649e
F ext/wasm/api/sqlite3-api-oo1.js 06ad2079368e16cb9f182c18cd37bdc3932536856dff4f60582d0ca5f6c491a8
F ext/wasm/api/sqlite3-api-prologue.js e63bcd1d1942d3313bd11e76ac1fccdd3e34ba54a48b8c8296db8dd892705dbc
F ext/wasm/api/sqlite3-api-prologue.js 04f789bab878ea67be8b18dd4292011bbfeb78ebad79eb79c591136332bd469a
F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f
F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3
F ext/wasm/api/sqlite3-opfs-async-proxy.js 9963c78bf6e5ccb5ba28e8597851bd9d980e86803b6d341cc985e586aef10c82
@ -521,7 +521,7 @@ F ext/wasm/c-pp.c 92285f7bce67ed7b7020b40fde8ed0982c442b63dc33df9dfd4b658d4a6c07
F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b
F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
F ext/wasm/common/testing.css 35889709547d89a6109ff83b25c11bbc91d8dd43aab8722e428655ca98880a06
F ext/wasm/common/whwasmutil.js 16a592d5c304a2d268ca1c28e08a5b029a2f3cbe10af78dbc3456cfc9e3559d1
F ext/wasm/common/whwasmutil.js dbe625a22bf0962cde1f958f2be604d27d2f97ee1b4e6ee0f19c6480aa36aeed
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6
@ -555,7 +555,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
F ext/wasm/test-opfs-vfs.js 44363db07b2a20e73b0eb1808de4400ca71b703af718d0fa6d962f15e73bf2ac
F ext/wasm/tester1-worker.html 5ef353348c37cf2e4fd0b23da562d3275523e036260b510734e9a3239ba8c987
F ext/wasm/tester1.c-pp.html 74aa9b31c75f12490653f814b53c3dd39f40cd3f70d6a53a716f4e8587107399
F ext/wasm/tester1.c-pp.js c39594bb1a0272a08a1544277a29c1ed920b4c4877611591e7c3185744ae2431
F ext/wasm/tester1.c-pp.js b6a8b9d8e8b070d40c140a68072bf0aa41819fe37fb5bbb9391966a3cbc154fa
F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70
F ext/wasm/tests/opfs/concurrency/test.js bfc3d7e27b207f0827f12568986b8d516a744529550b449314f5c21c9e9faf4a
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
@ -2065,8 +2065,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 6ee61f8cede4998f0c838d6506b058c6b09f34b3d7f30ed296100785c93f8d00
R ca2f65a200c7997e977e891739f47e7d
U drh
Z 02ee85efaa582407b1d21e8a02ad86a6
P 91f50964c10fb12d889bda7d597d8edf475d97d2d8b534b4400e0fed1d753c6a
R e4bc31bd87c945bd19fac07fc1661145
U stephan
Z a323b013b8116acefa1b2ce6dcb45815
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
91f50964c10fb12d889bda7d597d8edf475d97d2d8b534b4400e0fed1d753c6a
e1009b16d351b23676ad7bffab0c91b373a92132eb855c9af61991b50cd237ed