1
0
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:
stephan
2023-08-11 21:24:08 +00:00
25 changed files with 874 additions and 212 deletions

View File

@ -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):

View File

@ -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"]
];

View File

@ -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);
}
};

View File

@ -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){

View File

@ -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.

View File

@ -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',

View File

@ -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());