1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-05 15:55:57 +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

@@ -52,6 +52,13 @@ MINIMAL_AMALGAMATION = 0
USE_STDCALL = 0
!ENDIF
# Set this non-0 to use structured exception handling (SEH) for WAL mode
# in the core library.
#
!IFNDEF USE_SEH
USE_SEH = 0
!ENDIF
# Set this non-0 to have the shell executable link against the core dynamic
# link library.
#
@@ -389,6 +396,13 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MATH_FUNCTIONS
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
!ENDIF
# Should structured exception handling (SEH) be enabled for WAL mode in
# the core library?
#
!IF $(USE_SEH)!=0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_USE_SEH=1
!ENDIF
# These are the "extended" SQLite compilation options used when compiling for
# the Windows 10 platform.
#

View File

@@ -52,6 +52,13 @@ MINIMAL_AMALGAMATION = 0
USE_STDCALL = 0
!ENDIF
# Set this non-0 to use structured exception handling (SEH) for WAL mode
# in the core library.
#
!IFNDEF USE_SEH
USE_SEH = 0
!ENDIF
# Set this non-0 to have the shell executable link against the core dynamic
# link library.
#
@@ -311,6 +318,13 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MATH_FUNCTIONS
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
!ENDIF
# Should structured exception handling (SEH) be enabled for WAL mode in
# the core library?
#
!IF $(USE_SEH)!=0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_USE_SEH=1
!ENDIF
# These are the "extended" SQLite compilation options used when compiling for
# the Windows 10 platform.
#

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

View File

@@ -1,11 +1,11 @@
C Fix\sa\smakefile\sdeps\sproblem\swhich\scaused\sext/jni\sbuild\sto\sfail\sif\ssqlite3.c/h\swere\snot\screated\sbeforehand.
D 2023-08-11T20:32:40.205
C Merge\strunk\sinto\sjni\sbranch.
D 2023-08-11T21:24:08.276
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F Makefile.in 5ad2d1e198306bc730f06f7545d3a8832225b1bfddbc648d97c0e0b9a35f67e9
F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6
F Makefile.msc 7248d860f71ab164b4cec3c415e6cc1bd9fee860c370d65bd8bb49e9572521e2
F Makefile.msc daad4a19e0b3c3c3b79b64d4ddbf75e3f506405e8d3f3f604d6f48b26043c51f
F README.md c1c4218efcc4071a6e26db2b517fdbc1035696a29b370edd655faddbef02b224
F VERSION c6366dc72582d3144ce87b013cc35fe48d62f6d07d5be0c9716ea33c862144aa
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
@@ -15,7 +15,7 @@ F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903
F autoconf/Makefile.am a8d1d24affe52ebf8d7ddcf91aa973fa0316618ab95bb68c87cabf8faf527dc8
F autoconf/Makefile.fallback 22fe523eb36dfce31e0f6349f782eb084e86a5620b2b0b4f84a2d6133f53f5ac
F autoconf/Makefile.msc 20366d19fbfc3fceecce95344a2114069eaab4fc22e4f02430da167a1e2ddf04
F autoconf/Makefile.msc 00f11ce1f7904416fe841c33e7d789defe8c39e1df6b97e93ed2af3b1bbaf9d7
F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7
F autoconf/README.txt 42cfd21d0b19dc7d5d85fb5c405c5f3c6a4c923021c39128f6ba685355d8fd56
F autoconf/configure.ac ec7fa914c5e74ff212fe879f9bb6918e1234497e05facfb641f30c4d5893b277
@@ -528,7 +528,7 @@ F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
F ext/wasm/GNUmakefile 50a4bd40ee01a90badfc28d0042789740e47e2855d3b9acaa8801b6dc2763aba
F ext/wasm/GNUmakefile 8159bc5f9433fe21022c1a8e8c30cb1a523530ba9ef53bdf5d1e0a2186554806
F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576
F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab
@@ -541,16 +541,16 @@ 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 ad906703f7429590f2fbf5e6498513bf727a1a4f0ebfa057afb08161d7511219
F ext/wasm/api/sqlite3-api-cleanup.js d235ad237df6954145404305040991c72ef8b1881715d2a650dda7b3c2576d0e
F ext/wasm/api/sqlite3-api-glue.js cc6b0bb093bdb6279d4af259200b7b9e150e3796a8a3a4cd09a4928c43d25e56
F ext/wasm/api/sqlite3-api-glue.js b65e546568f1dfb35205b9792feb5146a6323d71b55cda58e2ed30def6dd52f3
F ext/wasm/api/sqlite3-api-oo1.js 9678dc4d9a5d39632b6ffe6ea94a023119260815bf32f265bf5f6c36c9516db8
F ext/wasm/api/sqlite3-api-prologue.js 76258e160bf6a89cc75a7d3c05646a054c8cab7219cd1e10bc20cacaad022131
F ext/wasm/api/sqlite3-api-prologue.js 5f283b096b98bfb1ee2f2201e7ff0489dff00e29e1030c30953bdb4f5b87f4bd
F ext/wasm/api/sqlite3-api-worker1.js 9f32af64df1a031071912eea7a201557fe39b1738645c0134562bb84e88e2fec
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256dfb4f96555b865dbb7a6b65e379
F ext/wasm/api/sqlite3-v-helper.js 7daa0eab0a513a25b05e9abae7b5beaaa39209b3ed12f86aeae9ef8d2719ed25
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js abb69b5e008961026bf5ff433d7116cb046359af92a5daf73208af2e7ac80ae7
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js e7a690e0e78ff4d563f2eca468f91db69f001ff4b79c6d2304cbb6f62dca437d
F ext/wasm/api/sqlite3-wasm.c 8867f1d41c112fb4a2cfe22ff224eccaf309fcdea266cee0ec554f85db72ef0f
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js e04fc2fda6a0200ef80efdbb4ddfa0254453558adb17ec3a230f93d2bf1d711c
F ext/wasm/api/sqlite3-wasm.c d4d4c2b349b43b7b861e6d2994299630fb79e07573ea6b61e28e8071b7d16b61
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f
F ext/wasm/api/sqlite3-worker1.c-pp.js da509469755035e919c015deea41b4514b5e84c12a1332e6cc8d42cb2cc1fb75
F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8
@@ -580,7 +580,7 @@ F ext/wasm/index-dist.html 22379774f0ad4edcaaa8cf9c674c82e794cc557719a8addabed74
F ext/wasm/index.html 4e7847b909f4ae0da8c829b150b79454050e53b3658431f138636257729cd42b
F ext/wasm/jaccwabyt/jaccwabyt.js 1264710db3cfbcb6887d95665b7aeba60c1126eaef789ca4cf1a4a17d5bc7f54
F ext/wasm/jaccwabyt/jaccwabyt.md 37911f00db12cbcca73aa1ed72594430365f30aafae2fa9c886961de74e5e0eb
F ext/wasm/module-symbols.html 841de62fc198988b8330e238c260e70ec93028b096e1a1234db31b187a899d10
F ext/wasm/module-symbols.html dc476b403369b26a1a23773e13b80f41b9a49f0825e81435fe3600a7cfbbe337
F ext/wasm/scratchpad-wasmfs.html a3d7388f3c4b263676b58b526846e9d02dfcb4014ff29d3a5040935286af5b96
F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd842231505895eff00dbd57c63
F ext/wasm/speedtest1-wasmfs.html 0e9d335a9b5b5fafe6e1bc8dc0f0ca7e22e6eb916682a2d7c36218bb7d67379d
@@ -595,7 +595,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b
F ext/wasm/tester1-worker.html ebc4b820a128963afce328ecf63ab200bd923309eb939f4110510ab449e9814c
F ext/wasm/tester1.c-pp.html 1c1bc78b858af2019e663b1a31e76657b73dc24bede28ca92fbe917c3a972af2
F ext/wasm/tester1.c-pp.js b88dcad5424a652e8204c44a71bbc3deb22a4922c97ba792aedbabb7a6827b91
F ext/wasm/tester1.c-pp.js 64eb0ee6e695d5638d0f758f31a0ca2231e627ca5d768de3d8b44f9f494de8d4
F ext/wasm/tests/opfs/concurrency/index.html 0802373d57034d51835ff6041cda438c7a982deea6079efd98098d3e42fbcbc1
F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
@@ -628,7 +628,7 @@ F src/btreeInt.h 91a9e0c41a0e71fa91a742ec285c63dd8dcb38b73d14fae0ed7209174ff0fdc
F src/build.c a8ae3b32d9aa9bbd2c0e97d7c0dd80def9fbca408425de1608f57ee6f47f45f4
F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c 20507cc0b0a6c19cd882fcd0eaeda32ae6a4229fb4b024cfdf3183043d9b703d
F src/ctime.c cff2b493de894383832347c3a3f936a05f4e089a92b42366838da1735f3848b5
F src/date.c f73f203b3877cef866c60ab402aec2bf89597219b60635cf50cbe3c5e4533e94
F src/dbpage.c f3eea5f7ec47e09ee7da40f42b25092ecbe961fc59566b8e5f705f34335b2387
F src/dbstat.c ec92074baa61d883de58c945162d9e666c13cd7cf3a23bc38b4d1c4d0b2c2bef
@@ -643,7 +643,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276
F src/json.c 9c231a853268ce6aee2e300e26d4445ba42117374a2275f8e9537b2f912909d6
F src/json.c ae840f87b418f039f5d336b488933d09396bd31e6b31e855b93055ccaee4e255
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 176d6b2cb18a6ad73b133db17f6fc351c4d9a2d510deebdb76c22bde9cfd1465
F src/main.c fde8f13c876a658b4e8b74b77d875ca887915c174ea6a2f3122d80966f93d865
@@ -670,8 +670,8 @@ F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d87210
F src/os_unix.c 2e8b12107f75d1bd16412f312b4c5d5103191807a37836d3b81beb26436ad81b
F src/os_win.c 7038223a1cda0a47e2ab4db47f63bf1833fe53ba0542f0f283a062ea13894103
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 5ddf3a74c633a008ea6b2f5b3186167e88e2c8ca8a252ecab06ab3f1eb48e60f
F src/pager.h f82e9844166e1585f5786837ddc7709966138ced17f568c16af7ccf946c2baa3
F src/pager.c 993445a19b611d473ca007542ab3149840661a4c7e9f2d9e1ec008b7cc2abe78
F src/pager.h 6e326bd05970a24dd28d41d3980b6964fbaa37b4da54a2c0d4e0c5bdb06ff187
F src/parse.y aeb7760d41cfa86465e3adba506500c021597049fd55f82a30e5b7045862c28c
F src/pcache.c 4cd4a0043167da9ba7e19b4d179a0e6354e7fe32c16f781ecf9bf0a5ff63b40b
F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
@@ -685,7 +685,7 @@ F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c 5f545a2c8702d4d3430bbb188cfec47d6c122d899061ef00cbe56af14591c574
F src/shell.c.in 694aaf751f00610381533d4a31c83d142cfc83ef91ef65e2aa6912ace7c39b40
F src/sqlite.h.in 7b07a33d2af82ee974aa91e6294abce0282b2f4c5934b291d2fff961810dd867
F src/sqlite.h.in 73a366c1c45d5ac9888cfe81c458826a44498531d106cfb4f328193ab5f6f17d
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4
F src/sqliteInt.h 025ed58a41968ef80d64cdc194caa8dd207b0256b147253d762fdac7a62408f9
@@ -745,19 +745,19 @@ F src/test_windirent.h da2e5b73c32d09905fbdd00f27cd802212a32a58ead882736fe4f5eb7
F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394ba3f
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 0fb405f9adf3f757c26bfc1ae6d58ac5dccbb918917ba9e5ef0e6673a06563d3
F src/tokenize.c 23d9f4539880b40226254ad9072f4ecf12eb1902e62aea47aac29928afafcfd5
F src/treeview.c 1d52fbc4e97161e65858d36e3424ea6e3fc045dd8a679c82b4b9593dc30de3bd
F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0
F src/update.c 0bb9171afaa4d0b100ad946873bccda7aef90ffe083ef5c63668fce08c4df9da
F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
F src/util.c b3532a95ad56db67b3acd3955e688e4cb80ebec6fd1f459a8eb51cceedd6de69
F src/util.c a40062117e705eb3339201842717a022092816b92479eead6397cde28af32ff9
F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104
F src/vdbe.c 346d848a0bf8128e3e3722c5406f4bde6c32d7093b93402c6f8e0718d19305c3
F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0
F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c
F src/vdbeapi.c f37822f215740ede2a8fcae99bc13f2cc3a72dd0e1d22b81b9298c5ca67dbc38
F src/vdbeaux.c a586f445945eef6ad1fcd7c94f700faa1baea93c0dbd446291c7cf65966c8470
F src/vdbeaux.c e3aa5c46827cd95e0fc4d0f302fa3e901ab5f07258fdbb42709eeef40f63018d
F src/vdbeblob.c 2516697b3ee8154eb8915f29466fb5d4f1ae39ee8b755ea909cefaf57ec5e2ce
F src/vdbemem.c 317b9f48708139db6239ade40c7980b4bc8233168383690d588dad6d8437f722
F src/vdbesort.c 0d40dca073c94e158ead752ef4225f4fee22dee84145e8c00ca2309afb489015
@@ -765,8 +765,8 @@ F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf8
F src/vdbevtab.c 57fa8f56478e5b5cb558cb425e7878515e0a105c54f96f1d1bbf4b9433529254
F src/vtab.c 1ecf8c3745d29275688d583e12822fa984d421e0286b5ef50c137bc3bf6d7a64
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c dd843f619ac60d5dadab7109cf402432ba74dde0c301505fd1c202add07659e3
F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
F src/wal.c 02e10f033a6972bc7d50122b400318003199c504cda48f61ad404564505f4e89
F src/wal.h 04a9e53121d5076f2a173b0f2facb39d33047093fee71bd3bbe6b1f6f1f5fd4b
F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2
F src/where.c b8917792f1e0dbfa28fb29e6cd3d560060d69667be0ba4c491cbc772363264f5
F src/whereInt.h c7d19902863beadec1d04e66aca39c0bcd60b74f05f0eaa7422c7005dfc5d51a
@@ -1272,7 +1272,7 @@ F test/json/README.md 63e3e589e1df8fd3cc1588ba1faaff659214003f8b77a15af5c6452b35
F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd28656fb261bddc8a3f
F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307
F test/json/json-speed-check.sh 8b7babf530faa58bd59d6d362cec8e9036a68c5457ff46f3b1f1511d21af6737 x
F test/json101.test 94126d4291d4a00e45f6988ce885c410de69243490e46e70e9946cb6e6f9ea02
F test/json101.test 243b0a2650218ac5eafde6ce2a92a0e9d02bf24f62aec68693b69d9a693f120a
F test/json102.test 24f6f204f9cde45b971016691d0b92a9b4c58040d699e36d6b12cb165f9083ff
F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe
F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1
@@ -1538,7 +1538,7 @@ F test/snapshot.test a504f2e7009f512ef66c719f0ea1c55a556bdaf1e1312c80a04d46fc1a3
F test/snapshot2.test 8d6ff5dd9cc503f6e12d408a30409c3f9c653507b24408d9cd7195931c89bc54
F test/snapshot3.test 8744313270c55f6e18574283553d3c5c5fe4c5970585663613a0e75c151e599b
F test/snapshot4.test d4e9347ef2fcabc491fc893506c7bbaf334da3be111d6eb4f3a97cc623b78322
F test/snapshot_fault.test f6c5ef7cb93bf92fbb4e864ecc5c87df7d3a250064838822db5b4d3a5563ede4
F test/snapshot_fault.test 129234ceb9b26a0e1000e8563a16e790f5c1412354e70749cbd78c3d5d07d60a
F test/snapshot_up.test a0a29c4cf33475fcef07c3f8e64af795e24ab91b4cc68295863402a393cdd41c
F test/soak.test 18944cf21b94a7fe0df02016a6ee1e9632bc4e8d095a0cb49d95e15d5cca2d5c
F test/softheap1.test 843cd84db9891b2d01b9ab64cef3e9020f98d087
@@ -1898,6 +1898,7 @@ F test/walprotocol2.test 7d3b6b4bf0b12f8007121b1e6ef714bc99101fb3b48e46371df1db8
F test/walro.test cb438d05ba0d191f10b688e39c4f0cd5b71569a1d1f4440e5bdf3c6880e08c20
F test/walro2.test 33955a6fd874dd9724005e17f77fef89d334b3171454a1256fe4941a96766cdc
F test/walrofault.test c70cb6e308c443867701856cce92ad8288cd99488fa52afab77cca6cfd51af68
F test/walseh1.test 82da37763b0d87942dccd191e58321532ce3d44b87ef36e04ff9ce13f382bbae
F test/walsetlk.test 34c901443b31ab720afc463f5b236c86ca5c4134402573dce91aa0761de8db5a
F test/walshared.test 42e3808582504878af237ea02c42ca793e8a0efaa19df7df26ac573370dbc7a3
F test/walslow.test c05c68d4dc2700a982f89133ce103a1a84cc285f
@@ -2012,7 +2013,7 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
F tool/merge-test.tcl de76b62f2de2a92d4c1ca4f976bce0aea6899e0229e250479b229b2a1914b176
F tool/mkautoconfamal.sh f62353eb6c06ab264da027fd4507d09914433dbdcab9cb011cdc18016f1ab3b8
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
F tool/mkctimec.tcl 38e3db33210a200aae791635125052a643a27aa0619a0debf19aa9c55e1b2dde x
F tool/mkctimec.tcl aca4b83e49aecf10368cd5d11bc4847061041ade026db5bd8da17ef201f1403b x
F tool/mkkeywordhash.c b9faa0ae7e14e4dbbcd951cddd786bf46b8a65bb07b129ba8c0cfade723aaffd
F tool/mkmsvcmin.tcl 8897d515ef7f94772322db95a3b6fce6c614d84fe0bdd06ba5a1c786351d5a1d
F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61a07ef
@@ -2090,8 +2091,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 6c8538d83495ce65dbd7417263b3b06dbbb2a649e9a61a743911944599d75ffc
R 00e08242dc8443215cfb70aa39f020b2
P 101de670774f63757180282763730aa53e70198bd7a674c27e6044632d39d22a 8a6b0c24937e855b710f97b4aea973eff53e6d43e1182842731547aa4b37db2a
R f0a8c88fe7b9660119e5c4d560adb197
U stephan
Z 4a14af7dfae41cdfaa811266b607e2bd
Z b7f921f442de7d419e4b8b5ee576c27d
# Remove this line to create a well-formed Fossil manifest.

View File

@@ -1 +1 @@
101de670774f63757180282763730aa53e70198bd7a674c27e6044632d39d22a
4f0aeeba0287e846908180eab6f7080ebe1323ebe49340771864d110e1ca5b2b

View File

@@ -60,9 +60,6 @@ static const char * const sqlite3azCompileOpt[] = {
#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC
"4_BYTE_ALIGNED_MALLOC",
#endif
#ifdef SQLITE_64BIT_STATS
"64BIT_STATS",
#endif
#ifdef SQLITE_ALLOW_COVERING_INDEX_SCAN
# if SQLITE_ALLOW_COVERING_INDEX_SCAN != 1
"ALLOW_COVERING_INDEX_SCAN=" CTIMEOPT_VAL(SQLITE_ALLOW_COVERING_INDEX_SCAN),
@@ -399,6 +396,9 @@ static const char * const sqlite3azCompileOpt[] = {
#ifdef SQLITE_INTEGRITY_CHECK_ERROR_MAX
"INTEGRITY_CHECK_ERROR_MAX=" CTIMEOPT_VAL(SQLITE_INTEGRITY_CHECK_ERROR_MAX),
#endif
#ifdef SQLITE_LEGACY_JSON_VALID
"LEGACY_JSON_VALID",
#endif
#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS
"LIKE_DOESNT_MATCH_BLOBS",
#endif

View File

@@ -3009,7 +3009,13 @@ static void jsonValidFunc(
){
JsonParse *p; /* The parse */
UNUSED_PARAMETER(argc);
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
#ifdef SQLITE_LEGACY_JSON_VALID
/* Incorrect legacy behavior was to return FALSE for a NULL input */
sqlite3_result_int(ctx, 0);
#endif
return;
}
p = jsonParseCached(ctx, argv[0], 0, 0);
if( p==0 || p->oom ){
sqlite3_result_error_nomem(ctx);

View File

@@ -7729,4 +7729,10 @@ int sqlite3PagerWalFramesize(Pager *pPager){
}
#endif
#ifdef SQLITE_USE_SEH
int sqlite3PagerWalSystemErrno(Pager *pPager){
return sqlite3WalSystemErrno(pPager->pWal);
}
#endif
#endif /* SQLITE_OMIT_DISKIO */

View File

@@ -240,4 +240,8 @@ void sqlite3PagerRekey(DbPage*, Pgno, u16);
# define enable_simulated_io_errors()
#endif
#ifdef SQLITE_USE_SEH
int sqlite3PagerWalSystemErrno(Pager*);
#endif
#endif /* SQLITE_PAGER_H */

View File

@@ -528,6 +528,7 @@ int sqlite3_exec(
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))

View File

@@ -433,7 +433,7 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' );
testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' );
testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' );
testcase( z[0]=='9' );
testcase( z[0]=='9' ); testcase( z[0]=='.' );
*tokenType = TK_INTEGER;
#ifndef SQLITE_OMIT_HEX_INTEGER
if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){

View File

@@ -141,6 +141,23 @@ void sqlite3ErrorClear(sqlite3 *db){
*/
void sqlite3SystemError(sqlite3 *db, int rc){
if( rc==SQLITE_IOERR_NOMEM ) return;
#ifdef SQLITE_USE_SEH
if( rc==SQLITE_IOERR_IN_PAGE ){
int ii;
int iErr;
sqlite3BtreeEnterAll(db);
for(ii=0; ii<db->nDb; ii++){
if( db->aDb[ii].pBt ){
iErr = sqlite3PagerWalSystemErrno(sqlite3BtreePager(db->aDb[ii].pBt));
if( iErr ){
db->iSysErrno = iErr;
}
}
}
sqlite3BtreeLeaveAll(db);
return;
}
#endif
rc &= 0xff;
if( rc==SQLITE_CANTOPEN || rc==SQLITE_IOERR ){
db->iSysErrno = sqlite3OsGetLastError(db->pVfs);

View File

@@ -3372,6 +3372,7 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3VdbeLeave(p);
return SQLITE_BUSY;
}else if( rc!=SQLITE_OK ){
sqlite3SystemError(db, rc);
p->rc = rc;
sqlite3RollbackAll(db, SQLITE_OK);
p->nChange = 0;

557
src/wal.c
View File

@@ -528,7 +528,13 @@ struct Wal {
u32 iReCksum; /* On commit, recalculate checksums from here */
const char *zWalName; /* Name of WAL file */
u32 nCkpt; /* Checkpoint sequence counter in the wal-header */
#ifdef SQLITE_USE_SEH
u32 lockMask; /* Mask of locks held */
void *pFree; /* Pointer to sqlite3_free() if exception thrown */
int iSysErrno; /* System error code following exception */
#endif
#ifdef SQLITE_DEBUG
int nSehTry; /* Number of nested SEH_TRY{} blocks */
u8 lockError; /* True if a locking error has occurred */
#endif
#ifdef SQLITE_ENABLE_SNAPSHOT
@@ -610,6 +616,100 @@ struct WalIterator {
sizeof(ht_slot)*HASHTABLE_NSLOT + HASHTABLE_NPAGE*sizeof(u32) \
)
/*
** Structured Exception Handling (SEH) is a Windows-specific technique
** for catching exceptions raised while accessing memory-mapped files.
**
** The -DSQLITE_USE_SEH compile-time option means to use SEH to catch and
** deal with system-level errors that arise during WAL -shm file processing.
** Without this compile-time option, any system-level faults that appear
** while accessing the memory-mapped -shm file will cause a process-wide
** signal to be deliver, which will more than likely cause the entire
** process to exit.
*/
#ifdef SQLITE_USE_SEH
#include <Windows.h>
/* Beginning of a block of code in which an exception might occur */
# define SEH_TRY __try { \
assert( walAssertLockmask(pWal) && pWal->nSehTry==0 ); \
VVA_ONLY(pWal->nSehTry++);
/* The end of a block of code in which an exception might occur */
# define SEH_EXCEPT(X) \
VVA_ONLY(pWal->nSehTry--); \
assert( pWal->nSehTry==0 ); \
} __except( sehExceptionFilter(pWal, GetExceptionCode(), GetExceptionInformation() ) ){ X }
/* Simulate a memory-mapping fault in the -shm file for testing purposes */
# define SEH_INJECT_FAULT sehInjectFault(pWal)
/*
** The second argument is the return value of GetExceptionCode() for the
** current exception. Return EXCEPTION_EXECUTE_HANDLER if the exception code
** indicates that the exception may have been caused by accessing the *-shm
** file mapping. Or EXCEPTION_CONTINUE_SEARCH otherwise.
*/
static int sehExceptionFilter(Wal *pWal, int eCode, EXCEPTION_POINTERS *p){
VVA_ONLY(pWal->nSehTry--);
if( eCode==EXCEPTION_IN_PAGE_ERROR ){
if( p && p->ExceptionRecord && p->ExceptionRecord->NumberParameters>=3 ){
/* From MSDN: For this type of exception, the first element of the
** ExceptionInformation[] array is a read-write flag - 0 if the exception
** was thrown while reading, 1 if while writing. The second element is
** the virtual address being accessed. The "third array element specifies
** the underlying NTSTATUS code that resulted in the exception". */
pWal->iSysErrno = (int)p->ExceptionRecord->ExceptionInformation[2];
}
return EXCEPTION_EXECUTE_HANDLER;
}
return EXCEPTION_CONTINUE_SEARCH;
}
/*
** If one is configured, invoke the xTestCallback callback with 650 as
** the argument. If it returns true, throw the same exception that is
** thrown by the system if the *-shm file mapping is accessed after it
** has been invalidated.
*/
static void sehInjectFault(Wal *pWal){
int res;
assert( pWal->nSehTry>0 );
res = sqlite3FaultSim(650);
if( res!=0 ){
ULONG_PTR aArg[3];
aArg[0] = 0;
aArg[1] = 0;
aArg[2] = (ULONG_PTR)res;
RaiseException(EXCEPTION_IN_PAGE_ERROR, 0, 3, (const ULONG_PTR*)aArg);
}
}
/*
** There are two ways to use this macro. To set a pointer to be freed
** if an exception is thrown:
**
** SEH_FREE_ON_ERROR(0, pPtr);
**
** and to cancel the same:
**
** SEH_FREE_ON_ERROR(pPtr, 0);
**
** In the first case, there must not already be a pointer registered to
** be freed. In the second case, pPtr must be the registered pointer.
*/
#define SEH_FREE_ON_ERROR(X,Y) \
assert( (X==0 || Y==0) && pWal->pFree==X ); pWal->pFree = Y
#else
# define SEH_TRY VVA_ONLY(pWal->nSehTry++);
# define SEH_EXCEPT(X) VVA_ONLY(pWal->nSehTry--); assert( pWal->nSehTry==0 );
# define SEH_INJECT_FAULT assert( pWal->nSehTry>0 );
# define SEH_FREE_ON_ERROR(X,Y)
#endif /* ifdef SQLITE_USE_SEH */
/*
** Obtain a pointer to the iPage'th page of the wal-index. The wal-index
** is broken into pages of WALINDEX_PGSZ bytes. Wal-index pages are
@@ -682,6 +782,7 @@ static int walIndexPage(
int iPage, /* The page we seek */
volatile u32 **ppPage /* Write the page pointer here */
){
SEH_INJECT_FAULT;
if( pWal->nWiData<=iPage || (*ppPage = pWal->apWiData[iPage])==0 ){
return walIndexPageRealloc(pWal, iPage, ppPage);
}
@@ -693,6 +794,7 @@ static int walIndexPage(
*/
static volatile WalCkptInfo *walCkptInfo(Wal *pWal){
assert( pWal->nWiData>0 && pWal->apWiData[0] );
SEH_INJECT_FAULT;
return (volatile WalCkptInfo*)&(pWal->apWiData[0][sizeof(WalIndexHdr)/2]);
}
@@ -701,6 +803,7 @@ static volatile WalCkptInfo *walCkptInfo(Wal *pWal){
*/
static volatile WalIndexHdr *walIndexHdr(Wal *pWal){
assert( pWal->nWiData>0 && pWal->apWiData[0] );
SEH_INJECT_FAULT;
return (volatile WalIndexHdr*)pWal->apWiData[0];
}
@@ -958,12 +1061,18 @@ static int walLockShared(Wal *pWal, int lockIdx){
WALTRACE(("WAL%p: acquire SHARED-%s %s\n", pWal,
walLockName(lockIdx), rc ? "failed" : "ok"));
VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); )
#ifdef SQLITE_USE_SEH
if( rc==SQLITE_OK ) pWal->lockMask |= (1 << lockIdx);
#endif
return rc;
}
static void walUnlockShared(Wal *pWal, int lockIdx){
if( pWal->exclusiveMode ) return;
(void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1,
SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED);
#ifdef SQLITE_USE_SEH
pWal->lockMask &= ~(1 << lockIdx);
#endif
WALTRACE(("WAL%p: release SHARED-%s\n", pWal, walLockName(lockIdx)));
}
static int walLockExclusive(Wal *pWal, int lockIdx, int n){
@@ -974,12 +1083,20 @@ static int walLockExclusive(Wal *pWal, int lockIdx, int n){
WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal,
walLockName(lockIdx), n, rc ? "failed" : "ok"));
VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); )
#ifdef SQLITE_USE_SEH
if( rc==SQLITE_OK ){
pWal->lockMask |= (((1<<n)-1) << (SQLITE_SHM_NLOCK+lockIdx));
}
#endif
return rc;
}
static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){
if( pWal->exclusiveMode ) return;
(void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n,
SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE);
#ifdef SQLITE_USE_SEH
pWal->lockMask &= ~(((1<<n)-1) << (SQLITE_SHM_NLOCK+lockIdx));
#endif
WALTRACE(("WAL%p: release EXCLUSIVE-%s cnt=%d\n", pWal,
walLockName(lockIdx), n));
}
@@ -1071,6 +1188,7 @@ static int walFramePage(u32 iFrame){
*/
static u32 walFramePgno(Wal *pWal, u32 iFrame){
int iHash = walFramePage(iFrame);
SEH_INJECT_FAULT;
if( iHash==0 ){
return pWal->apWiData[0][WALINDEX_HDR_SIZE/sizeof(u32) + iFrame - 1];
}
@@ -1330,6 +1448,7 @@ static int walIndexRecover(Wal *pWal){
/* Malloc a buffer to read frames into. */
szFrame = szPage + WAL_FRAME_HDRSIZE;
aFrame = (u8 *)sqlite3_malloc64(szFrame + WALINDEX_PGSZ);
SEH_FREE_ON_ERROR(0, aFrame);
if( !aFrame ){
rc = SQLITE_NOMEM_BKPT;
goto recovery_error;
@@ -1405,9 +1524,11 @@ static int walIndexRecover(Wal *pWal){
}
}
#endif
SEH_INJECT_FAULT;
if( iFrame<=iLast ) break;
}
SEH_FREE_ON_ERROR(aFrame, 0);
sqlite3_free(aFrame);
}
@@ -1435,6 +1556,7 @@ finished:
}else{
pInfo->aReadMark[i] = READMARK_NOT_USED;
}
SEH_INJECT_FAULT;
walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
}else if( rc!=SQLITE_BUSY ){
goto recovery_error;
@@ -1827,6 +1949,7 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
memset(p, 0, nByte);
p->nSegment = nSegment;
aTmp = (ht_slot*)&(((u8*)p)[nByte]);
SEH_FREE_ON_ERROR(0, p);
for(i=walFramePage(nBackfill+1); rc==SQLITE_OK && i<nSegment; i++){
WalHashLoc sLoc;
@@ -1855,6 +1978,7 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
}
}
if( rc!=SQLITE_OK ){
SEH_FREE_ON_ERROR(p, 0);
walIteratorFree(p);
p = 0;
}
@@ -2080,13 +2204,13 @@ static int walCheckpoint(
mxSafeFrame = pWal->hdr.mxFrame;
mxPage = pWal->hdr.nPage;
for(i=1; i<WAL_NREADER; i++){
u32 y = AtomicLoad(pInfo->aReadMark+i);
u32 y = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT;
if( mxSafeFrame>y ){
assert( y<=pWal->hdr.mxFrame );
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
if( rc==SQLITE_OK ){
u32 iMark = (i==1 ? mxSafeFrame : READMARK_NOT_USED);
AtomicStore(pInfo->aReadMark+i, iMark);
AtomicStore(pInfo->aReadMark+i, iMark); SEH_INJECT_FAULT;
walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
}else if( rc==SQLITE_BUSY ){
mxSafeFrame = y;
@@ -2107,8 +2231,7 @@ static int walCheckpoint(
&& (rc = walBusyLock(pWal,xBusy,pBusyArg,WAL_READ_LOCK(0),1))==SQLITE_OK
){
u32 nBackfill = pInfo->nBackfill;
pInfo->nBackfillAttempted = mxSafeFrame;
pInfo->nBackfillAttempted = mxSafeFrame; SEH_INJECT_FAULT;
/* Sync the WAL to disk */
rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags));
@@ -2139,6 +2262,7 @@ static int walCheckpoint(
while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
i64 iOffset;
assert( walFramePgno(pWal, iFrame)==iDbpage );
SEH_INJECT_FAULT;
if( AtomicLoad(&db->u1.isInterrupted) ){
rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT;
break;
@@ -2168,7 +2292,7 @@ static int walCheckpoint(
}
}
if( rc==SQLITE_OK ){
AtomicStore(&pInfo->nBackfill, mxSafeFrame);
AtomicStore(&pInfo->nBackfill, mxSafeFrame); SEH_INJECT_FAULT;
}
}
@@ -2190,6 +2314,7 @@ static int walCheckpoint(
*/
if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){
assert( pWal->writeLock );
SEH_INJECT_FAULT;
if( pInfo->nBackfill<pWal->hdr.mxFrame ){
rc = SQLITE_BUSY;
}else if( eMode>=SQLITE_CHECKPOINT_RESTART ){
@@ -2221,6 +2346,7 @@ static int walCheckpoint(
}
walcheckpoint_out:
SEH_FREE_ON_ERROR(pIter, 0);
walIteratorFree(pIter);
return rc;
}
@@ -2243,6 +2369,87 @@ static void walLimitSize(Wal *pWal, i64 nMax){
}
}
#ifdef SQLITE_USE_SEH
/*
** This is the "standard" exception handler used in a few places to handle
** an exception thrown by reading from the *-shm mapping after it has become
** invalid in SQLITE_USE_SEH builds. It is used as follows:
**
** SEH_TRY { ... }
** SEH_EXCEPT( rc = walHandleException(pWal); )
**
** This function does three things:
**
** 1) Determines the locks that should be held, based on the contents of
** the Wal.readLock, Wal.writeLock and Wal.ckptLock variables. All other
** held locks are assumed to be transient locks that would have been
** released had the exception not been thrown and are dropped.
**
** 2) Frees the pointer at Wal.pFree, if any, using sqlite3_free().
**
** 3) Returns SQLITE_IOERR.
*/
static int walHandleException(Wal *pWal){
if( pWal->exclusiveMode==0 ){
static const int S = 1;
static const int E = (1<<SQLITE_SHM_NLOCK);
int ii;
u32 mUnlock = pWal->lockMask & ~(
(pWal->readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock)))
| (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0)
| (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0)
);
for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
if( (S<<ii) & mUnlock ) walUnlockShared(pWal, ii);
if( (E<<ii) & mUnlock ) walUnlockExclusive(pWal, ii, 1);
}
}
sqlite3_free(pWal->pFree);
pWal->pFree = 0;
return SQLITE_IOERR_IN_PAGE;
}
/*
** Assert that the Wal.lockMask mask, which indicates the locks held
** by the connenction, is consistent with the Wal.readLock, Wal.writeLock
** and Wal.ckptLock variables. To be used as:
**
** assert( walAssertLockmask(pWal) );
*/
static int walAssertLockmask(Wal *pWal){
if( pWal->exclusiveMode==0 ){
static const int S = 1;
static const int E = (1<<SQLITE_SHM_NLOCK);
u32 mExpect = (
(pWal->readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock)))
| (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0)
| (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0)
#ifdef SQLITE_ENABLE_SNAPSHOT
| (pWal->pSnapshot ? (pWal->lockMask & (1 << WAL_CKPT_LOCK)) : 0)
#endif
);
assert( mExpect==pWal->lockMask );
}
return 1;
}
/*
** Return and zero the "system error" field set when an
** EXCEPTION_IN_PAGE_ERROR exception is caught.
*/
int sqlite3WalSystemErrno(Wal *pWal){
int iRet = 0;
if( pWal ){
iRet = pWal->iSysErrno;
pWal->iSysErrno = 0;
}
return iRet;
}
#else
# define walAssertLockmask(x) 1
#endif /* ifdef SQLITE_USE_SEH */
/*
** Close a connection to a log file.
*/
@@ -2257,6 +2464,8 @@ int sqlite3WalClose(
if( pWal ){
int isDelete = 0; /* True to unlink wal and wal-index files */
assert( walAssertLockmask(pWal) );
/* If an EXCLUSIVE lock can be obtained on the database file (using the
** ordinary, rollback-mode locking methods, this guarantees that the
** connection associated with this log file is the only connection to
@@ -2799,6 +3008,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
assert( pWal->nWiData>0 );
assert( pWal->apWiData[0]!=0 );
pInfo = walCkptInfo(pWal);
SEH_INJECT_FAULT;
if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame
#ifdef SQLITE_ENABLE_SNAPSHOT
&& (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0)
@@ -2848,7 +3058,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
}
#endif
for(i=1; i<WAL_NREADER; i++){
u32 thisMark = AtomicLoad(pInfo->aReadMark+i);
u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT;
if( mxReadMark<=thisMark && thisMark<=mxFrame ){
assert( thisMark!=READMARK_NOT_USED );
mxReadMark = thisMark;
@@ -2914,7 +3124,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** we can guarantee that the checkpointer that set nBackfill could not
** see any pages past pWal->hdr.mxFrame, this problem does not come up.
*/
pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1;
pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; SEH_INJECT_FAULT;
walShmBarrier(pWal);
if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark
|| memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
@@ -2929,6 +3139,53 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
}
#ifdef SQLITE_ENABLE_SNAPSHOT
/*
** This function does the work of sqlite3WalSnapshotRecover().
*/
static int walSnapshotRecover(
Wal *pWal, /* WAL handle */
void *pBuf1, /* Temp buffer pWal->szPage bytes in size */
void *pBuf2 /* Temp buffer pWal->szPage bytes in size */
){
int szPage = (int)pWal->szPage;
int rc;
i64 szDb; /* Size of db file in bytes */
rc = sqlite3OsFileSize(pWal->pDbFd, &szDb);
if( rc==SQLITE_OK ){
volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
u32 i = pInfo->nBackfillAttempted;
for(i=pInfo->nBackfillAttempted; i>AtomicLoad(&pInfo->nBackfill); i--){
WalHashLoc sLoc; /* Hash table location */
u32 pgno; /* Page number in db file */
i64 iDbOff; /* Offset of db file entry */
i64 iWalOff; /* Offset of wal file entry */
rc = walHashGet(pWal, walFramePage(i), &sLoc);
if( rc!=SQLITE_OK ) break;
pgno = sLoc.aPgno[i-sLoc.iZero];
iDbOff = (i64)(pgno-1) * szPage;
if( iDbOff+szPage<=szDb ){
iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE;
rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff);
if( rc==SQLITE_OK ){
rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff);
}
if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){
break;
}
}
pInfo->nBackfillAttempted = i-1;
}
}
return rc;
}
/*
** Attempt to reduce the value of the WalCkptInfo.nBackfillAttempted
** variable so that older snapshots can be accessed. To do this, loop
@@ -2954,50 +3211,21 @@ int sqlite3WalSnapshotRecover(Wal *pWal){
assert( pWal->readLock>=0 );
rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
if( rc==SQLITE_OK ){
volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
int szPage = (int)pWal->szPage;
i64 szDb; /* Size of db file in bytes */
rc = sqlite3OsFileSize(pWal->pDbFd, &szDb);
if( rc==SQLITE_OK ){
void *pBuf1 = sqlite3_malloc(szPage);
void *pBuf2 = sqlite3_malloc(szPage);
if( pBuf1==0 || pBuf2==0 ){
rc = SQLITE_NOMEM;
}else{
u32 i = pInfo->nBackfillAttempted;
for(i=pInfo->nBackfillAttempted; i>AtomicLoad(&pInfo->nBackfill); i--){
WalHashLoc sLoc; /* Hash table location */
u32 pgno; /* Page number in db file */
i64 iDbOff; /* Offset of db file entry */
i64 iWalOff; /* Offset of wal file entry */
rc = walHashGet(pWal, walFramePage(i), &sLoc);
if( rc!=SQLITE_OK ) break;
assert( i - sLoc.iZero - 1 >=0 );
pgno = sLoc.aPgno[i-sLoc.iZero-1];
iDbOff = (i64)(pgno-1) * szPage;
if( iDbOff+szPage<=szDb ){
iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE;
rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff);
if( rc==SQLITE_OK ){
rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff);
}
if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){
break;
}
}
pInfo->nBackfillAttempted = i-1;
}
void *pBuf1 = sqlite3_malloc(pWal->szPage);
void *pBuf2 = sqlite3_malloc(pWal->szPage);
if( pBuf1==0 || pBuf2==0 ){
rc = SQLITE_NOMEM;
}else{
pWal->ckptLock = 1;
SEH_TRY {
rc = walSnapshotRecover(pWal, pBuf1, pBuf2);
}
sqlite3_free(pBuf1);
sqlite3_free(pBuf2);
SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
pWal->ckptLock = 0;
}
sqlite3_free(pBuf1);
sqlite3_free(pBuf2);
walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
}
@@ -3006,28 +3234,20 @@ int sqlite3WalSnapshotRecover(Wal *pWal){
#endif /* SQLITE_ENABLE_SNAPSHOT */
/*
** Begin a read transaction on the database.
**
** This routine used to be called sqlite3OpenSnapshot() and with good reason:
** it takes a snapshot of the state of the WAL and wal-index for the current
** instant in time. The current thread will continue to use this snapshot.
** Other threads might append new content to the WAL and wal-index but
** that extra content is ignored by the current thread.
**
** If the database contents have changes since the previous read
** transaction, then *pChanged is set to 1 before returning. The
** Pager layer will use this to know that its cache is stale and
** needs to be flushed.
** This function does the work of sqlite3WalBeginReadTransaction() (see
** below). That function simply calls this one inside an SEH_TRY{...} block.
*/
int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
static int walBeginReadTransaction(Wal *pWal, int *pChanged){
int rc; /* Return code */
int cnt = 0; /* Number of TryBeginRead attempts */
#ifdef SQLITE_ENABLE_SNAPSHOT
int ckptLock = 0;
int bChanged = 0;
WalIndexHdr *pSnapshot = pWal->pSnapshot;
#endif
assert( pWal->ckptLock==0 );
assert( pWal->nSehTry>0 );
#ifdef SQLITE_ENABLE_SNAPSHOT
if( pSnapshot ){
@@ -3050,7 +3270,7 @@ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
if( rc!=SQLITE_OK ){
return rc;
}
pWal->ckptLock = 1;
ckptLock = 1;
}
#endif
@@ -3114,15 +3334,37 @@ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
}
/* Release the shared CKPT lock obtained above. */
if( pWal->ckptLock ){
if( ckptLock ){
assert( pSnapshot );
walUnlockShared(pWal, WAL_CKPT_LOCK);
pWal->ckptLock = 0;
}
#endif
return rc;
}
/*
** Begin a read transaction on the database.
**
** This routine used to be called sqlite3OpenSnapshot() and with good reason:
** it takes a snapshot of the state of the WAL and wal-index for the current
** instant in time. The current thread will continue to use this snapshot.
** Other threads might append new content to the WAL and wal-index but
** that extra content is ignored by the current thread.
**
** If the database contents have changes since the previous read
** transaction, then *pChanged is set to 1 before returning. The
** Pager layer will use this to know that its cache is stale and
** needs to be flushed.
*/
int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
int rc;
SEH_TRY {
rc = walBeginReadTransaction(pWal, pChanged);
}
SEH_EXCEPT( rc = walHandleException(pWal); )
return rc;
}
/*
** Finish with a read transaction. All this does is release the
** read-lock.
@@ -3143,7 +3385,7 @@ void sqlite3WalEndReadTransaction(Wal *pWal){
** Return SQLITE_OK if successful, or an error code if an error occurs. If an
** error does occur, the final value of *piRead is undefined.
*/
int sqlite3WalFindFrame(
static int walFindFrame(
Wal *pWal, /* WAL handle */
Pgno pgno, /* Database page number to read data for */
u32 *piRead /* OUT: Frame number (or zero) */
@@ -3206,6 +3448,7 @@ int sqlite3WalFindFrame(
}
nCollide = HASHTABLE_NSLOT;
iKey = walHash(pgno);
SEH_INJECT_FAULT;
while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){
u32 iFrame = iH + sLoc.iZero;
if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH-1]==pgno ){
@@ -3242,6 +3485,30 @@ int sqlite3WalFindFrame(
return SQLITE_OK;
}
/*
** Search the wal file for page pgno. If found, set *piRead to the frame that
** contains the page. Otherwise, if pgno is not in the wal file, set *piRead
** to zero.
**
** Return SQLITE_OK if successful, or an error code if an error occurs. If an
** error does occur, the final value of *piRead is undefined.
**
** The difference between this function and walFindFrame() is that this
** function wraps walFindFrame() in an SEH_TRY{...} block.
*/
int sqlite3WalFindFrame(
Wal *pWal, /* WAL handle */
Pgno pgno, /* Database page number to read data for */
u32 *piRead /* OUT: Frame number (or zero) */
){
int rc;
SEH_TRY {
rc = walFindFrame(pWal, pgno, piRead);
}
SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
return rc;
}
/*
** Read the contents of frame iRead from the wal file into buffer pOut
** (which is nOut bytes in size). Return SQLITE_OK if successful, or an
@@ -3323,12 +3590,17 @@ int sqlite3WalBeginWriteTransaction(Wal *pWal){
** time the read transaction on this connection was started, then
** the write is disallowed.
*/
if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
SEH_TRY {
if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
rc = SQLITE_BUSY_SNAPSHOT;
}
}
SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
if( rc!=SQLITE_OK ){
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
pWal->writeLock = 0;
rc = SQLITE_BUSY_SNAPSHOT;
}
return rc;
}
@@ -3364,30 +3636,33 @@ int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
Pgno iMax = pWal->hdr.mxFrame;
Pgno iFrame;
/* Restore the clients cache of the wal-index header to the state it
** was in before the client began writing to the database.
*/
memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr));
for(iFrame=pWal->hdr.mxFrame+1;
ALWAYS(rc==SQLITE_OK) && iFrame<=iMax;
iFrame++
){
/* This call cannot fail. Unless the page for which the page number
** is passed as the second argument is (a) in the cache and
** (b) has an outstanding reference, then xUndo is either a no-op
** (if (a) is false) or simply expels the page from the cache (if (b)
** is false).
**
** If the upper layer is doing a rollback, it is guaranteed that there
** are no outstanding references to any page other than page 1. And
** page 1 is never written to the log until the transaction is
** committed. As a result, the call to xUndo may not fail.
SEH_TRY {
/* Restore the clients cache of the wal-index header to the state it
** was in before the client began writing to the database.
*/
assert( walFramePgno(pWal, iFrame)!=1 );
rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame));
memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr));
for(iFrame=pWal->hdr.mxFrame+1;
ALWAYS(rc==SQLITE_OK) && iFrame<=iMax;
iFrame++
){
/* This call cannot fail. Unless the page for which the page number
** is passed as the second argument is (a) in the cache and
** (b) has an outstanding reference, then xUndo is either a no-op
** (if (a) is false) or simply expels the page from the cache (if (b)
** is false).
**
** If the upper layer is doing a rollback, it is guaranteed that there
** are no outstanding references to any page other than page 1. And
** page 1 is never written to the log until the transaction is
** committed. As a result, the call to xUndo may not fail.
*/
assert( walFramePgno(pWal, iFrame)!=1 );
rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame));
}
if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal);
}
if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal);
SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
}
return rc;
}
@@ -3431,7 +3706,10 @@ int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
pWal->hdr.mxFrame = aWalData[0];
pWal->hdr.aFrameCksum[0] = aWalData[1];
pWal->hdr.aFrameCksum[1] = aWalData[2];
walCleanupHash(pWal);
SEH_TRY {
walCleanupHash(pWal);
}
SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
}
return rc;
@@ -3612,7 +3890,7 @@ static int walRewriteChecksums(Wal *pWal, u32 iLast){
** Write a set of frames to the log. The caller must hold the write-lock
** on the log file (obtained using sqlite3WalBeginWriteTransaction()).
*/
int sqlite3WalFrames(
static int walFrames(
Wal *pWal, /* Wal handle to write to */
int szPage, /* Database page-size in bytes */
PgHdr *pList, /* List of dirty pages to write */
@@ -3723,7 +4001,7 @@ int sqlite3WalFrames(
** checksums must be recomputed when the transaction is committed. */
if( iFirst && (p->pDirty || isCommit==0) ){
u32 iWrite = 0;
VVA_ONLY(rc =) sqlite3WalFindFrame(pWal, p->pgno, &iWrite);
VVA_ONLY(rc =) walFindFrame(pWal, p->pgno, &iWrite);
assert( rc==SQLITE_OK || iWrite==0 );
if( iWrite>=iFirst ){
i64 iOff = walFrameOffset(iWrite, szPage) + WAL_FRAME_HDRSIZE;
@@ -3842,6 +4120,29 @@ int sqlite3WalFrames(
return rc;
}
/*
** Write a set of frames to the log. The caller must hold the write-lock
** on the log file (obtained using sqlite3WalBeginWriteTransaction()).
**
** The difference between this function and walFrames() is that this
** function wraps walFrames() in an SEH_TRY{...} block.
*/
int sqlite3WalFrames(
Wal *pWal, /* Wal handle to write to */
int szPage, /* Database page-size in bytes */
PgHdr *pList, /* List of dirty pages to write */
Pgno nTruncate, /* Database size after this commit */
int isCommit, /* True if this is a commit */
int sync_flags /* Flags to pass to OsSync() (or 0) */
){
int rc;
SEH_TRY {
rc = walFrames(pWal, szPage, pList, nTruncate, isCommit, sync_flags);
}
SEH_EXCEPT( rc = walHandleException(pWal); )
return rc;
}
/*
** This routine is called to implement sqlite3_wal_checkpoint() and
** related interfaces.
@@ -3921,30 +4222,33 @@ int sqlite3WalCheckpoint(
/* Read the wal-index header. */
if( rc==SQLITE_OK ){
walDisableBlocking(pWal);
rc = walIndexReadHdr(pWal, &isChanged);
(void)walEnableBlocking(pWal);
if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
}
}
/* Copy data from the log to the database file. */
if( rc==SQLITE_OK ){
if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
rc = SQLITE_CORRUPT_BKPT;
}else{
rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags, zBuf);
}
/* If no error occurred, set the output variables. */
if( rc==SQLITE_OK || rc==SQLITE_BUSY ){
if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame;
if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill);
SEH_TRY {
if( rc==SQLITE_OK ){
walDisableBlocking(pWal);
rc = walIndexReadHdr(pWal, &isChanged);
(void)walEnableBlocking(pWal);
if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
}
}
/* Copy data from the log to the database file. */
if( rc==SQLITE_OK ){
if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
rc = SQLITE_CORRUPT_BKPT;
}else{
rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags,zBuf);
}
/* If no error occurred, set the output variables. */
if( rc==SQLITE_OK || rc==SQLITE_BUSY ){
if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame;
SEH_INJECT_FAULT;
if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill);
}
}
}
SEH_EXCEPT( rc = walHandleException(pWal); )
if( isChanged ){
/* If a new wal-index header was loaded before the checkpoint was
@@ -4021,7 +4325,9 @@ int sqlite3WalExclusiveMode(Wal *pWal, int op){
** locks are taken in this case). Nor should the pager attempt to
** upgrade to exclusive-mode following such an error.
*/
#ifndef SQLITE_USE_SEH
assert( pWal->readLock>=0 || pWal->lockError );
#endif
assert( pWal->readLock>=0 || (op<=0 && pWal->exclusiveMode==0) );
if( op==0 ){
@@ -4122,16 +4428,19 @@ int sqlite3_snapshot_cmp(sqlite3_snapshot *p1, sqlite3_snapshot *p2){
*/
int sqlite3WalSnapshotCheck(Wal *pWal, sqlite3_snapshot *pSnapshot){
int rc;
rc = walLockShared(pWal, WAL_CKPT_LOCK);
if( rc==SQLITE_OK ){
WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot;
if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
|| pNew->mxFrame<walCkptInfo(pWal)->nBackfillAttempted
){
rc = SQLITE_ERROR_SNAPSHOT;
walUnlockShared(pWal, WAL_CKPT_LOCK);
SEH_TRY {
rc = walLockShared(pWal, WAL_CKPT_LOCK);
if( rc==SQLITE_OK ){
WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot;
if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
|| pNew->mxFrame<walCkptInfo(pWal)->nBackfillAttempted
){
rc = SQLITE_ERROR_SNAPSHOT;
walUnlockShared(pWal, WAL_CKPT_LOCK);
}
}
}
SEH_EXCEPT( rc = walHandleException(pWal); )
return rc;
}

View File

@@ -151,5 +151,9 @@ int sqlite3WalWriteLock(Wal *pWal, int bLock);
void sqlite3WalDb(Wal *pWal, sqlite3 *db);
#endif
#ifdef SQLITE_USE_SEH
int sqlite3WalSystemErrno(Wal*);
#endif
#endif /* ifndef SQLITE_OMIT_WAL */
#endif /* SQLITE_WAL_H */

View File

@@ -923,9 +923,15 @@ do_execsql_test json-20.3 {
# a NULL value as the JSON input.
#
db null NULL
do_execsql_test json-21.1 {
SELECT json_valid(NULL);
} NULL
if {[db exists {SELECT * FROM pragma_compile_options WHERE compile_options LIKE '%legacy_json_valid%'}]} {
do_execsql_test json-21.1-legacy {
SELECT json_valid(NULL);
} 0
} else {
do_execsql_test json-21.1-correct {
SELECT json_valid(NULL);
} NULL
}
do_execsql_test json-21.2 {
SELECT json_error_position(NULL);
} NULL

View File

@@ -23,6 +23,7 @@ set testprefix snapshot_fault
# checkpointing the db.
#
do_faultsim_test 1.0 -prep {
catch { db2 close }
faultsim_delete_and_reopen
sqlite3 db2 test.db
db2 eval {

150
test/walseh1.test Normal file
View File

@@ -0,0 +1,150 @@
# 2021 August 16
#
# 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.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
set testprefix walseh1
set ::seh_countdown 0
set ::seh_errno 10
proc seh_faultsim_callback {iFault} {
if {$iFault==650} {
incr ::seh_countdown -1
if {$::seh_countdown==0} { return $::seh_errno }
}
return 0
}
proc seh_injectinstall {} {
sqlite3_test_control_fault_install seh_faultsim_callback
}
proc seh_injectuninstall {} {
sqlite3_test_control_fault_install
}
proc seh_injectstart {iFail} {
set ::seh_errno [expr 2+$iFail*10]
set ::seh_countdown $iFail
}
proc seh_injectstop {} {
set res [expr $::seh_countdown<=0]
set ::seh_countdown 0
set res
}
set FAULTSIM(seh) [list \
-injectinstall seh_injectinstall \
-injectstart seh_injectstart \
-injectstop seh_injectstop \
-injecterrlist {{1 {disk I/O error}}} \
-injectuninstall seh_injectuninstall \
]
proc test_system_errno {db expect} {
set serrno [sqlite3_system_errno $db]
if {$serrno!=$expect} {
error "surprising system_errno. Expected $expect, got $serrno"
}
}
do_execsql_test 1.0 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(x, y);
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(3, 4);
} {wal}
faultsim_save_and_close
do_faultsim_test 1 -faults seh -prep {
catch { db2 close }
faultsim_restore_and_reopen
execsql { SELECT * FROM sqlite_schema }
sqlite3 db2 test.db
} -body {
execsql { SELECT * FROM t1 } db2
} -test {
faultsim_test_result {0 {1 2 3 4}}
if {$testrc} { test_system_errno db2 $::seh_errno }
}
catch { db2 close }
faultsim_save_and_close
do_faultsim_test 2 -faults seh -prep {
catch { db close }
faultsim_restore_and_reopen
} -body {
execsql { SELECT * FROM t1 }
} -test {
faultsim_test_result {0 {1 2 3 4}}
if {$testrc} { test_system_errno db $::seh_errno }
}
do_faultsim_test 3 -faults seh -prep {
catch { db close }
faultsim_restore_and_reopen
} -body {
execsql { INSERT INTO t1 VALUES(5, 6) }
execsql { SELECT * FROM t1 }
} -test {
faultsim_test_result {0 {1 2 3 4 5 6}}
if {$testrc} { test_system_errno db $::seh_errno }
}
do_faultsim_test 4 -faults seh -prep {
catch { db close }
faultsim_restore_and_reopen
} -body {
execsql { PRAGMA wal_checkpoint }
execsql { INSERT INTO t1 VALUES(7, 8) }
execsql { SELECT * FROM t1 }
} -test {
faultsim_test_result {0 {1 2 3 4 7 8}}
if {$testrc} { test_system_errno db $::seh_errno }
}
catch { db close }
do_faultsim_test 5 -faults seh -prep {
catch { db close }
faultsim_restore_and_reopen
execsql {
PRAGMA cache_size = 5;
BEGIN;
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50
)
INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM s;
}
} -body {
execsql ROLLBACK
} -test {
faultsim_test_result {0 {}}
if {$testrc} { test_system_errno db $::seh_errno }
}
catch { db close }
do_faultsim_test 6 -faults seh -prep {
catch { db close }
faultsim_restore_and_reopen
} -body {
execsql { PRAGMA wal_checkpoint = TRUNCATE }
execsql { INSERT INTO t1 VALUES(7, 8) }
execsql { SELECT * FROM t1 }
} -test {
faultsim_test_result {0 {1 2 3 4 7 8}}
if {$testrc} { test_system_errno db $::seh_errno }
}
catch { db close }
finish_test

View File

@@ -180,6 +180,7 @@ set boolean_defnil_options {
SQLITE_IGNORE_FLOCK_LOCK_ERRORS
SQLITE_INLINE_MEMCPY
SQLITE_INT64_TYPE
SQLITE_LEGACY_JSON_VALID
SQLITE_LIKE_DOESNT_MATCH_BLOBS
SQLITE_LOCK_TRACE
SQLITE_LOG_CACHE_SPILL