}
const pDb = this.pointer;
Object.keys(__stmtMap.get(this)).forEach((k,s)=>{
- if(s && s.pointer) s.finalize();
+ if(s && s.pointer){
+ try{s.finalize()}
+ catch(e){/*ignore*/}
+ }
});
__ptrMap.delete(this);
__stmtMap.delete(this);
and names. */) ? 0 : 1;
evalFirstResult = false;
if(arg.cbArg || resultRows){
- for(; stmt.step(); stmt._isLocked = false){
+ for(; stmt.step(); stmt._lockedByExec = false){
if(0===gotColNames++) stmt.getColumnNames(opt.columnNames);
- stmt._isLocked = true;
+ stmt._lockedByExec = true;
const row = arg.cbArg(stmt);
if(resultRows) resultRows.push(row);
if(callback && false === callback.call(opt, row, stmt)){
break;
}
}
- stmt._isLocked = false;
+ stmt._lockedByExec = false;
}
if(0===gotColNames){
/* opt.columnNames was provided but we visited no result rows */
}else{
stmt.step();
}
- stmt.finalize();
+ stmt.reset(
+ /* In order to trigger an exception in the
+ INSERT...RETURNING locking scenario:
+ https://sqlite.org/forum/forumpost/36f7a2e7494897df
+ */).finalize();
stmt = null;
- }
+ }/*prepare() loop*/
}/*catch(e){
sqlite3.config.warn("DB.exec() is propagating exception",opt,e);
throw e;
}*/finally{
wasm.scopedAllocPop(stack);
if(stmt){
- delete stmt._isLocked;
+ delete stmt._lockedByExec;
stmt.finalize();
}
}
};
/**
- If stmt._isLocked is truthy, this throws an exception
+ If stmt._lockedByExec is truthy, this throws an exception
complaining that the 2nd argument (an operation name,
e.g. "bind()") is not legal while the statement is "locked".
Locking happens before an exec()-like callback is passed a
statement, to ensure that the callback does not mutate or
finalize the statement. If it does not throw, it returns stmt.
*/
- const affirmUnlocked = function(stmt,currentOpName){
- if(stmt._isLocked){
+ const affirmNotLockedByExec = function(stmt,currentOpName){
+ if(stmt._lockedByExec){
toss3("Operation is illegal when statement is locked:",currentOpName);
}
return stmt;
success.
*/
const bindOne = function f(stmt,ndx,bindType,val){
- affirmUnlocked(affirmStmtOpen(stmt), 'bind()');
+ affirmNotLockedByExec(affirmStmtOpen(stmt), 'bind()');
if(!f._){
f._tooBigInt = (v)=>toss3(
"BigInt value is too big to store without precision loss:", v
);
- /* Reminder: when not in BigInt mode, it's impossible for
- JS to represent a number out of the range we can bind,
- so we have no range checking. */
f._ = {
string: function(stmt, ndx, val, asBlob){
const [pStr, n] = wasm.allocCString(val, true);
Stmt.prototype = {
/**
- "Finalizes" this statement. This is a no-op if the
- statement has already been finalized. Returns
- undefined. Most methods in this class will throw if called
- after this is.
+ "Finalizes" this statement. This is a no-op if the statement
+ has already been finalized. Returns the result of
+ sqlite3_finalize() (0 on success, non-0 on error), or the
+ undefined value if the statement has already been
+ finalized. Regardless of success or failure, most methods in
+ this class will throw if called after this is.
This method always throws if called when it is illegal to do
- so, e.g. from a per-row callback handler of a DB.exec() call.
-
- As of versions 3.42.1 and 3.43, this method will throw by
- default if sqlite3_finalize() returns an error code, which can
- happen in certain unusual cases involving locking. When it
- throws for this reason, throwing is delayed until after all
- resources are cleaned up. That is, the finalization still runs
- to completion.
+ so. Namely, when triggered via a per-row callback handler of a
+ DB.exec() call.
*/
finalize: function(){
if(this.pointer){
- affirmUnlocked(this,'finalize()');
- const rc = capi.sqlite3_finalize(this.pointer),
- db = this.db;
- delete __stmtMap.get(db)[this.pointer];
+ affirmNotLockedByExec(this,'finalize()');
+ const rc = capi.sqlite3_finalize(this.pointer);
+ delete __stmtMap.get(this.db)[this.pointer];
__ptrMap.delete(this);
delete this._mayGet;
delete this.parameterCount;
- delete this._isLocked;
+ delete this._lockedByExec;
delete this.db;
- checkSqlite3Rc(db, rc);
+ return rc;
}
},
/**
a DB.exec() call).
*/
clearBindings: function(){
- affirmUnlocked(affirmStmtOpen(this), 'clearBindings()')
+ affirmNotLockedByExec(affirmStmtOpen(this), 'clearBindings()')
capi.sqlite3_clear_bindings(this.pointer);
this._mayGet = false;
return this;
/**
Resets this statement so that it may be step()ed again from the
beginning. Returns this object. Throws if this statement has
- been finalized or if it may not legally be reset because it is
- currently being used from a DB.exec() callback.
+ been finalized, if it may not legally be reset because it is
+ currently being used from a DB.exec() callback, or if the
+ underlying call to sqlite3_reset() returns non-0.
If passed a truthy argument then this.clearBindings() is
also called, otherwise any existing bindings, along with
any memory allocated for them, are retained.
- As of versions 3.42.1 and 3.43, this function throws if the
- underlying call to sqlite3_reset() returns non-0. That is
- necessary for catching errors in certain locking-related cases.
+ In verions 3.42.0 and earlier, this function did not throw if
+ sqlite3_reset() returns non-0, but it was discovered that
+ throwing (or significant extra client-side code) is necessary
+ in order to avoid certain silent failure scenarios, as
+ discussed at:
+
+ https://sqlite.org/forum/forumpost/36f7a2e7494897df
*/
reset: function(alsoClearBinds){
- affirmUnlocked(this,'reset()');
+ affirmNotLockedByExec(this,'reset()');
if(alsoClearBinds) this.clearBindings();
const rc = capi.sqlite3_reset(affirmStmtOpen(this).pointer);
this._mayGet = false;
value is returned. Throws on error.
*/
step: function(){
- affirmUnlocked(this, 'step()');
+ affirmNotLockedByExec(this, 'step()');
const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer);
switch(rc){
case capi.SQLITE_DONE: return this._mayGet = false;
```
*/
stepFinalize: function(){
- let rc, err;
try{
- rc = this.step();
- }catch(e){
- err = e;
- }
- if(err){
+ return this.step();
+ }finally{
try{this.finalize()}
- catch(x){/*ignored*/}
- throw err;
- }else{
- this.finalize();
+ catch(e){/*ignored*/}
}
- return rc;
},
/**
Fetches the value from the given 0-based column index of
-C sqlite3.oo1.Stmt.reset()\sand\sfinalize()\snow\sthrow\sif\stheir\sunderlying\sC-level\sAPIs\sreturn\snon-0,\sin\sorder\sto\savoid\ssilent\sfailure\sin\scertain\slocking-related\scases.
-D 2023-05-19T16:39:02.716
+C oo1.Stmt.finalize()\sno\slonger\sthrows,\sbut\sinstead\sreturns\sthe\ssame\sas\sthe\sC\sAPI.\soo1.DB.exec()\snow\striggers\sthe\sINSERT...RETURNING\slocking\sfailure\sas\san\sexception\svia\sreset()\sinstead\sof\sfinalize().\sSome\scode-adjacent\sinternal\sAPI\srenaming\sfor\sclarity's\ssake.
+D 2023-05-19T17:52:24.569
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/wasm/api/pre-js.c-pp.js ad906703f7429590f2fbf5e6498513bf727a1a4f0ebfa057afb08161d7511219
F ext/wasm/api/sqlite3-api-cleanup.js cc21e3486da748463e02bbe51e2464c6ac136587cdfd5aa00cd0b5385f6ca808
F ext/wasm/api/sqlite3-api-glue.js f1b2dcb944de5138bb5bd9a1559d2e76a4f3ec25260963d709e8237476688803
-F ext/wasm/api/sqlite3-api-oo1.js ac488a0e04d8b247f6cbc1b13b6ba54fac52342a80a00e1d6b17eab12f46aa59
+F ext/wasm/api/sqlite3-api-oo1.js c6a258fce7d833d48220d7e4a18512fa16f79e501e4fd371e280cdab842cd4c0
F ext/wasm/api/sqlite3-api-prologue.js 17f4ec398ba34c5c666fea8e8c4eb82064a35b302f2f2eb355283cd8d3f68ed5
F ext/wasm/api/sqlite3-api-worker1.js 40a5b1813fcbe789f23ae196c833432c8c83e7054d660194ddfc51eab1c5b9bf
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
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 8f3507b22f829818a37fc44ffa81e84b4192a2ebde6a351d14b9009e136883d9
+F ext/wasm/tester1.c-pp.js 6722037cdafb25ad0415f4561bd216427b36a45ff9f51096c370e374725ba3a9
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
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 3bb1d7b376b740c1a7bca82a8908840a9e6e9c1474c4f0fedf3d7eb917305b99
-Q +487ae12c9a21e5862bd590bbb1030c39734657d52136cf67b98c7545e6ecbe1c
-Q +d29d62cf7658aeb49f3c8a5d0b0809d945ebc9b79379a255eb88f771d2a2c430
-Q +f23eb5c6d36546ee1e181a03660e0b2dc8005bba24bee8bae594b0c78bd152cd
-R 516f611572496d6abedf96adce1c0fa8
+P db36a9ef5958cd761ca5bde697cc7cd83328dcc24d818023b813f6f860052754
+Q +4ee6b3aa531b980acea4c4b58ee256e765c5105100468928def3d4c9825fa9bc
+R 6ebf125e5dc2c49dd5a49418de89c8ad
U stephan
-Z d47c990271222ef793409d1dd7931763
+Z 945dcd8e12e5cd02ef0bc8b720828276
# Remove this line to create a well-formed Fossil manifest.