mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-01 06:27:03 +03:00
Merge trunk into jni branch.
FossilOrigin-Name: 4f0aeeba0287e846908180eab6f7080ebe1323ebe49340771864d110e1ca5b2b
This commit is contained in:
@ -179,7 +179,6 @@ SQLITE_OPT = \
|
||||
-DSQLITE_OMIT_DEPRECATED \
|
||||
-DSQLITE_OMIT_UTF16 \
|
||||
-DSQLITE_OMIT_SHARED_CACHE \
|
||||
-DSQLITE_OMIT_WAL \
|
||||
-DSQLITE_THREADSAFE=0 \
|
||||
-DSQLITE_TEMP_STORE=2 \
|
||||
-DSQLITE_OS_KV_OPTIONAL=1 \
|
||||
@ -187,6 +186,12 @@ SQLITE_OPT = \
|
||||
-DSQLITE_USE_URI=1 \
|
||||
-DSQLITE_WASM_ENABLE_C_TESTS \
|
||||
-DSQLITE_C=$(sqlite3.c)
|
||||
#SQLITE_OPT += -DSQLITE_DEBUG
|
||||
# Enabling SQLITE_DEBUG will break sqlite3_wasm_vfs_create_file()
|
||||
# (and thus sqlite3_js_vfs_create_file()). Those functions are
|
||||
# deprecated and alternatives are in place, but this crash behavior
|
||||
# can be used to find errant uses of sqlite3_js_vfs_create_file()
|
||||
# in client code.
|
||||
|
||||
.NOTPARALLEL: $(sqlite3.h)
|
||||
$(sqlite3.h):
|
||||
|
@ -608,6 +608,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
["sqlite3_wasm_db_vfs", "sqlite3_vfs*", "sqlite3*","string"],
|
||||
["sqlite3_wasm_vfs_create_file", "int",
|
||||
"sqlite3_vfs*","string","*", "int"],
|
||||
["sqlite3_wasm_posix_create_file", "int", "string","*", "int"],
|
||||
["sqlite3_wasm_vfs_unlink", "int", "sqlite3_vfs*","string"]
|
||||
];
|
||||
|
||||
|
@ -1357,6 +1357,74 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
};
|
||||
|
||||
/**
|
||||
If the current environment supports the POSIX file APIs, this routine
|
||||
creates (or overwrites) the given file using those APIs. This is
|
||||
primarily intended for use in Emscripten-based builds where the POSIX
|
||||
APIs are transparently proxied by an in-memory virtual filesystem.
|
||||
It may behave diffrently in other environments.
|
||||
|
||||
The first argument must be either a JS string or WASM C-string
|
||||
holding the filename. Note that this routine does _not_ create
|
||||
intermediary directories if the filename has a directory part.
|
||||
|
||||
The 2nd argument may either a valid WASM memory pointer, an
|
||||
ArrayBuffer, or a Uint8Array. The 3rd must be the length, in
|
||||
bytes, of the data array to copy. If the 2nd argument is an
|
||||
ArrayBuffer or Uint8Array and the 3rd is not a positive integer
|
||||
then the 3rd defaults to the array's byteLength value.
|
||||
|
||||
Results are undefined if data is a WASM pointer and dataLen is
|
||||
exceeds data's bounds.
|
||||
|
||||
Throws if any arguments are invalid or if creating or writing to
|
||||
the file fails.
|
||||
|
||||
Added in 3.43 as an alternative for the deprecated
|
||||
sqlite3_js_vfs_create_file().
|
||||
*/
|
||||
capi.sqlite3_js_posix_create_file = function(filename, data, dataLen){
|
||||
let pData;
|
||||
if(data && wasm.isPtr(data)){
|
||||
pData = data;
|
||||
}else if(data instanceof ArrayBuffer || data instanceof Uint8Array){
|
||||
pData = wasm.allocFromTypedArray(data);
|
||||
if(arguments.length<3 || !util.isInt32(dataLen) || dataLen<0){
|
||||
dataLen = data.byteLength;
|
||||
}
|
||||
}else{
|
||||
SQLite3Error.toss("Invalid 2nd argument for sqlite3_js_posix_create_file().");
|
||||
}
|
||||
try{
|
||||
if(!util.isInt32(dataLen) || dataLen<0){
|
||||
SQLite3Error.toss("Invalid 3rd argument for sqlite3_js_posix_create_file().");
|
||||
}
|
||||
const rc = wasm.sqlite3_wasm_posix_create_file(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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Deprecation warning: this function does not work properly in
|
||||
debug builds of sqlite3 because its out-of-scope use of the
|
||||
sqlite3_vfs API triggers assertions in the core library. That
|
||||
was unfortunately not discovered until 2023-08-11. This function
|
||||
is now deprecated and should not be used in new code.
|
||||
|
||||
Alternative options:
|
||||
|
||||
- "unix" VFS and its variants can get equivalent functionality
|
||||
with sqlite3_js_posix_create_file().
|
||||
|
||||
- OPFS: use either sqlite3.oo1.OpfsDb.importDb(), for the "opfs"
|
||||
VFS, or the importDb() method of the PoolUtil object provided
|
||||
by the "opfs-sahpool" OPFS (noting that its VFS name may differ
|
||||
depending on client-side configuration). We cannot proxy those
|
||||
from here because the former is necessarily asynchronous and
|
||||
the latter requires information not available to this function.
|
||||
|
||||
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
|
||||
@ -1402,9 +1470,13 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
VFS nor the WASM environment imposes requirements which break it.
|
||||
|
||||
- "opfs": uses OPFS storage and creates directory parts of the
|
||||
filename.
|
||||
filename. It can only be used to import an SQLite3 database
|
||||
file and will fail if given anything else.
|
||||
*/
|
||||
capi.sqlite3_js_vfs_create_file = function(vfs, filename, data, dataLen){
|
||||
config.warn("sqlite3_js_vfs_create_file() is deprecated and",
|
||||
"should be avoided because it can lead to C-level crashes.",
|
||||
"See its documentation for alternative options.");
|
||||
let pData;
|
||||
if(data){
|
||||
if(wasm.isPtr(data)){
|
||||
@ -1432,7 +1504,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
if(rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code",
|
||||
capi.sqlite3_js_rc_str(rc));
|
||||
}finally{
|
||||
wasm.dealloc(pData);
|
||||
wasm.dealloc(pData);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1168,11 +1168,41 @@ const installOpfsVfs = function callee(options){
|
||||
doDir(opt.directory, 0);
|
||||
};
|
||||
|
||||
//TODO to support fiddle and worker1 db upload:
|
||||
//opfsUtil.createFile = function(absName, content=undefined){...}
|
||||
//We have sqlite3.wasm.sqlite3_wasm_vfs_create_file() for this
|
||||
//purpose but its interface and name are still under
|
||||
//consideration.
|
||||
/**
|
||||
Asynchronously imports the given bytes (a byte array or
|
||||
ArrayBuffer) into the given database file.
|
||||
|
||||
It very specifically requires the input to be an SQLite3
|
||||
database and throws if that's not the case. It does so in
|
||||
order to prevent this function from taking on a larger scope
|
||||
than it is specifically intended to. i.e. we do not want it to
|
||||
become a convenience for importing arbitrary files into OPFS.
|
||||
|
||||
Throws on error. Resolves to the number of bytes written.
|
||||
*/
|
||||
opfsUtil.importDb = async function(filename, bytes){
|
||||
if(bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes);
|
||||
const n = bytes.byteLength;
|
||||
if(n<512 || n%512!=0){
|
||||
toss("Byte array size is invalid for an SQLite db.");
|
||||
}
|
||||
const header = "SQLite format 3";
|
||||
for(let i = 0; i < header.length; ++i){
|
||||
if( header.charCodeAt(i) !== bytes[i] ){
|
||||
toss("Input does not contain an SQLite database header.");
|
||||
}
|
||||
}
|
||||
const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true);
|
||||
const hFile = await hDir.getFileHandle(fnamePart, {create:true});
|
||||
const sah = await hFile.createSyncAccessHandle();
|
||||
sah.truncate(0);
|
||||
const nWrote = sah.write(bytes, {at: 0});
|
||||
sah.close();
|
||||
if(nWrote != n){
|
||||
toss("Expected to write "+n+" bytes but wrote "+nWrote+".");
|
||||
}
|
||||
return nWrote;
|
||||
};
|
||||
|
||||
if(sqlite3.oo1){
|
||||
const OpfsDb = function(...args){
|
||||
@ -1182,6 +1212,7 @@ const installOpfsVfs = function callee(options){
|
||||
};
|
||||
OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype);
|
||||
sqlite3.oo1.OpfsDb = OpfsDb;
|
||||
OpfsDb.importDb = opfsUtil.importDb;
|
||||
sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenSql(
|
||||
opfsVfs.pointer,
|
||||
function(oo1Db, sqlite3){
|
||||
|
@ -141,9 +141,6 @@
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
# define SQLITE_OMIT_UTF16 1
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
# define SQLITE_OMIT_WAL 1
|
||||
#endif
|
||||
#ifndef SQLITE_OS_KV_OPTIONAL
|
||||
# define SQLITE_OS_KV_OPTIONAL 1
|
||||
#endif
|
||||
@ -295,7 +292,7 @@ SQLITE_WASM_EXPORT void * sqlite3_wasm_pstack_ptr(void){
|
||||
*/
|
||||
SQLITE_WASM_EXPORT void sqlite3_wasm_pstack_restore(unsigned char * p){
|
||||
assert(p>=PStack.pBegin && p<=PStack.pEnd && p>=PStack.pPos);
|
||||
assert(0==(p & 0x7));
|
||||
assert(0==((unsigned long long)p & 0x7));
|
||||
if(p>=PStack.pBegin && p<=PStack.pEnd /*&& p>=PStack.pPos*/){
|
||||
PStack.pPos = p;
|
||||
}
|
||||
@ -1353,6 +1350,13 @@ int sqlite3_wasm_db_serialize( sqlite3 *pDb, const char *zSchema,
|
||||
** This function is NOT part of the sqlite3 public API. It is strictly
|
||||
** for use by the sqlite project's own JS/WASM bindings.
|
||||
**
|
||||
** ACHTUNG: it was discovered on 2023-08-11 that, with SQLITE_DEBUG,
|
||||
** this function's out-of-scope use of the sqlite3_vfs/file/io_methods
|
||||
** APIs leads to triggering of assertions in the core library. Its use
|
||||
** is now deprecated and VFS-specific APIs for importing files need to
|
||||
** be found to replace it. sqlite3_wasm_posix_create_file() is
|
||||
** suitable for the "unix" family of VFSes.
|
||||
**
|
||||
** 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
|
||||
@ -1398,7 +1402,14 @@ int sqlite3_wasm_vfs_create_file( sqlite3_vfs *pVfs,
|
||||
int rc;
|
||||
sqlite3_file *pFile = 0;
|
||||
sqlite3_io_methods const *pIo;
|
||||
const int openFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
|
||||
const int openFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE
|
||||
#if 0 && defined(SQLITE_DEBUG)
|
||||
| SQLITE_OPEN_MAIN_JOURNAL
|
||||
/* ^^^^ This is for testing a horrible workaround to avoid
|
||||
triggering a specific assert() in os_unix.c:unixOpen(). Please
|
||||
do not enable this in real builds. */
|
||||
#endif
|
||||
;
|
||||
int flagsOut = 0;
|
||||
int fileExisted = 0;
|
||||
int doUnlock = 0;
|
||||
@ -1464,6 +1475,34 @@ int sqlite3_wasm_vfs_create_file( sqlite3_vfs *pVfs,
|
||||
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.
|
||||
**
|
||||
** Creates or overwrites a file using the POSIX file API,
|
||||
** i.e. Emscripten's virtual filesystem. Creates or truncates
|
||||
** zFilename, appends pData bytes to it, and returns 0 on success or
|
||||
** SQLITE_IOERR on error.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3_wasm_posix_create_file( const char *zFilename,
|
||||
const unsigned char * pData,
|
||||
int nData ){
|
||||
int rc;
|
||||
FILE * pFile = 0;
|
||||
int fileExisted = 0;
|
||||
size_t nWrote = 1;
|
||||
|
||||
if( !zFilename || nData<0 || (pData==0 && nData>0) ) return SQLITE_MISUSE;
|
||||
pFile = fopen(zFilename, "w");
|
||||
if( 0==pFile ) return SQLITE_IOERR;
|
||||
if( nData>0 ){
|
||||
nWrote = fwrite(pData, (size_t)nData, 1, pFile);
|
||||
}
|
||||
fclose(pFile);
|
||||
return 1==nWrote ? 0 : SQLITE_IOERR;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is NOT part of the sqlite3 public API. It is strictly
|
||||
** for use by the sqlite project's own JS/WASM bindings.
|
||||
|
@ -355,6 +355,7 @@
|
||||
sqlite3_js_db_vfs: 'wasm:/api-c-style.md#sqlite3_js_db_vfs',
|
||||
sqlite3_js_kvvfs_clear: 'wasm:/api-c-style.md#sqlite3_js_kvvfs',
|
||||
sqlite3_js_kvvfs_size: 'wasm:/api-c-style.md#sqlite3_js_kvvfs',
|
||||
sqlite3_js_posix_create_file: 'wasm:/api-c-style.md#sqlite3_js_posix_create_file',
|
||||
sqlite3_js_rc_str: 'wasm:/api-c-style.md#sqlite3_js_rc_str',
|
||||
sqlite3_js_vfs_create_file: 'wasm:/api-c-style.md#sqlite3_js_vfs_create_file',
|
||||
sqlite3_js_vfs_list: 'wasm:/api-c-style.md#sqlite3_js_vfs_list',
|
||||
|
@ -1657,7 +1657,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
.assert('number'===typeof rc[0])
|
||||
.assert(rc[0]|0 === rc[0] /* is small integer */);
|
||||
})
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
.t({
|
||||
name: 'sqlite3_js_db_export()',
|
||||
predicate: ()=>true,
|
||||
@ -1671,13 +1671,12 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
}
|
||||
}/*sqlite3_js_db_export()*/)
|
||||
.t({
|
||||
name: 'sqlite3_js_vfs_create_file() with db in default VFS',
|
||||
name: 'sqlite3_js_posix_create_file()',
|
||||
predicate: ()=>true,
|
||||
test: function(sqlite3){
|
||||
const db = this.db;
|
||||
const pVfs = capi.sqlite3_js_db_vfs(db);
|
||||
const filename = "sqlite3_js_vfs_create_file().db";
|
||||
capi.sqlite3_js_vfs_create_file(pVfs, filename, this.dbExport);
|
||||
const filename = "sqlite3_js_posix_create_file.db";
|
||||
capi.sqlite3_js_posix_create_file(filename, this.dbExport);
|
||||
delete this.dbExport;
|
||||
const db2 = new sqlite3.oo1.DB(filename,'r');
|
||||
try {
|
||||
@ -1686,7 +1685,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
T.assert(n>0 && db2.selectValue(sql) === n);
|
||||
}finally{
|
||||
db2.close();
|
||||
wasm.sqlite3_wasm_vfs_unlink(pVfs, filename);
|
||||
wasm.sqlite3_wasm_vfs_unlink(0, filename);
|
||||
}
|
||||
}
|
||||
}/*sqlite3_js_vfs_create_file()*/)
|
||||
@ -2907,7 +2906,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
.t({
|
||||
name: 'OPFS db sanity checks',
|
||||
test: async function(sqlite3){
|
||||
const filename = this.opfsDbFile = 'sqlite3-tester1.db';
|
||||
const filename = this.opfsDbFile = '/dir/sqlite3-tester1.db';
|
||||
const pVfs = this.opfsVfs = capi.sqlite3_vfs_find('opfs');
|
||||
T.assert(pVfs);
|
||||
const unlink = this.opfsUnlink =
|
||||
@ -2935,22 +2934,23 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
}
|
||||
}/*OPFS db sanity checks*/)
|
||||
.t({
|
||||
name: 'OPFS export/import',
|
||||
name: 'OPFS import',
|
||||
test: async function(sqlite3){
|
||||
let db;
|
||||
try {
|
||||
const exp = this.opfsDbExport;
|
||||
delete this.opfsDbExport;
|
||||
capi.sqlite3_js_vfs_create_file("opfs", this.opfsDbFile, exp);
|
||||
const db = new sqlite3.oo1.OpfsDb(this.opfsDbFile);
|
||||
T.assert(6 === db.selectValue('select count(*) from p'));
|
||||
this.opfsImportSize = await sqlite3.oo1.OpfsDb.importDb(this.opfsDbFile, exp);
|
||||
db = new sqlite3.oo1.OpfsDb(this.opfsDbFile);
|
||||
T.assert(6 === db.selectValue('select count(*) from p')).
|
||||
assert( this.opfsImportSize == exp.byteLength );
|
||||
}finally{
|
||||
if(db) db.close();
|
||||
}
|
||||
}
|
||||
}/*OPFS export/import*/)
|
||||
.t({
|
||||
name: 'OPFS utility APIs and sqlite3_js_vfs_create_file()',
|
||||
name: '(Internal-use) OPFS utility APIs',
|
||||
test: async function(sqlite3){
|
||||
const filename = this.opfsDbFile;
|
||||
const pVfs = this.opfsVfs;
|
||||
@ -2959,8 +2959,6 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
delete this.opfsDbFile;
|
||||
delete this.opfsVfs;
|
||||
delete this.opfsUnlink;
|
||||
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
|
||||
@ -2968,39 +2966,19 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
any time.
|
||||
***************************************************************/
|
||||
const opfs = sqlite3.opfs;
|
||||
const fSize = 1379;
|
||||
const fSize = this.opfsImportSize;
|
||||
delete this.opfsImportSize;
|
||||
let sh;
|
||||
try{
|
||||
T.assert(!(await opfs.entryExists(filename)));
|
||||
capi.sqlite3_js_vfs_create_file(
|
||||
pVfs, filename, null, fSize
|
||||
);
|
||||
T.assert(await opfs.entryExists(filename));
|
||||
let fh = await opfs.rootDirectory.getFileHandle(filename);
|
||||
const [dirHandle, filenamePart] = await opfs.getDirForFilename(filename, false);
|
||||
const fh = await dirHandle.getFileHandle(filenamePart);
|
||||
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
|
||||
);
|
||||
}, "SQLITE_NOTFOUND: Unknown sqlite3_vfs name: no-such-vfs");
|
||||
}finally{
|
||||
if(sh) await sh.close();
|
||||
unlink();
|
||||
@ -3103,7 +3081,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
const conf2 = JSON.parse(JSON.stringify(sahPoolConfig));
|
||||
conf2.name += '-test2';
|
||||
const POther = await inst(conf2);
|
||||
log("Installed second SAH instance as",conf2.name);
|
||||
//log("Installed second SAH instance as",conf2.name);
|
||||
T.assert(0 === POther.getFileCount())
|
||||
.assert(true === await POther.removeVfs());
|
||||
|
||||
|
Reference in New Issue
Block a user