enabling clients to unambiguously identify such exceptions.
*/
class SQLite3Error extends Error {
+ /**
+ Constructs this object with a message equal to all arguments
+ concatenated with a space between each one.
+ */
constructor(...args){
- super(...args);
+ super(args.join(' '));
this.name = 'SQLite3Error';
}
};
- const toss3 = (...args)=>{throw new SQLite3Error(args)};
+ const toss3 = (...args)=>{throw new SQLite3Error(...args)};
sqlite3.SQLite3Error = SQLite3Error;
/**
};
/**
- Expects to be passed (arguments) from DB.exec() and
+ Expects to be passed the `arguments` object from DB.exec() and
DB.execMulti(). Does the argument processing/validation, throws
on error, and returns a new object on success:
/**
Starts a transaction, calls the given callback, and then either
- rolls back or commits the transaction, depending on whether the
- callback throw. The callback is pass this db object as its only
- argument. On success, returns the result of the callback.
- Throws on error.
+ rolls back or commits the savepoint, depending on whether the
+ callback throws. The callback is passed this db object as its
+ only argument. On success, returns the result of the
+ callback. Throws on error.
+
+ Note that transactions may not be nested, so this will throw if
+ it is called recursively. For nested transactions, use the
+ savepoint() method or manually manage SAVEPOINTs using exec().
*/
- callInTransaction: function(callback){
- affirmDbOpen(this);
- let err, rc;
- this.exec("BEGIN");
- try { rc = callback(this); }
- catch(e){
- err = e;
+ transaction: function(callback){
+ affirmDbOpen(this).exec("BEGIN");
+ try {
+ const rc = callback(this);
+ this.exec("COMMIT");
+ return rc;
+ }catch(e){
+ this.exec("ROLLBACK");
throw e;
- }finally{
- if(err) this.exec("ROLLBACK");
- else this.exec("COMMIT");
}
- return rc;
},
+ /**
+ This works similarly to transaction() but uses sqlite3's SAVEPOINT
+ feature. This function starts a savepoint (with an unspecified name)
+ and calls the given callback function, passing it this db object.
+ If the callback returns, the savepoint is released (committed). If
+ the callback throws, the savepoint is rolled back. If it does not
+ throw, it returns the result of the callback.
+ */
+ savepoint: function(callback){
+ affirmDbOpen(this).exec("SAVEPOINT oo1");
+ try {
+ const rc = callback(this);
+ this.exec("RELEASE oo1");
+ return rc;
+ }catch(e){
+ this.execMulti("ROLLBACK to SAVEPOINT oo1; RELEASE SAVEPOINT oo1");
+ throw e;
+ }
+ },
+
/**
This function currently does nothing and always throws. It
WILL BE REMOVED pending other refactoring, to eliminate a hard
oo = sqlite3.oo1,
wasm = capi.wasm;
- // If we have persistent storage, maybe init and mount it:
- const dbDir = true
- ? "" // this demo works better without persistent storage.
- : capi.sqlite3_web_persistent_dir();
- // ^^^ returns name of persistent mount point or "" if we have none
-
+ const dbDir = 1 ? "" : capi.sqlite3_web_persistent_dir();
const db = new oo.DB(dbDir+"/mydb.sqlite3");
+ log("db =",db.filename);
/**
Never(!) rely on garbage collection to clean up DBs and
(especially) statements. Always wrap their lifetimes in
}
try {
- db.callInTransaction( function(D) {
+ db.transaction( function(D) {
D.exec("delete from t");
log("In transaction: count(*) from t =",db.selectValue("select count(*) from t"));
- throw new sqlite3.SQLite3Error("Demonstrating callInTransaction() rollback");
+ throw new sqlite3.SQLite3Error("Demonstrating transaction() rollback");
+ });
+ }catch(e){
+ if(e instanceof sqlite3.SQLite3Error){
+ log("Got expected exception from db.transaction():",e.message);
+ log("count(*) from t =",db.selectValue("select count(*) from t"));
+ }else{
+ throw e;
+ }
+ }
+
+ try {
+ db.savepoint( function(D) {
+ D.exec("delete from t");
+ log("In savepoint: count(*) from t =",db.selectValue("select count(*) from t"));
+ D.savepoint(function(DD){
+ const rows = [];
+ D.execMulti({
+ sql: ["insert into t(a,b) values(99,100);",
+ "select count(*) from t"],
+ rowMode: 0,
+ resultRows: rows
+ });
+ log("In nested savepoint. Row count =",rows[0]);
+ throw new sqlite3.SQLite3Error("Demonstrating nested savepoint() rollback");
+ })
});
}catch(e){
if(e instanceof sqlite3.SQLite3Error){
- log("Got expected exception:",e.message);
+ log("Got expected exception from nested db.savepoint():",e.message);
log("count(*) from t =",db.selectValue("select count(*) from t"));
}else{
throw e;
const runDemos = function(Module){
//log("Module",Module);
const sqlite3 = Module.sqlite3,
- capi = sqlite3.capi,
- oo = sqlite3.oo1,
- wasm = capi.wasm;
+ capi = sqlite3.capi;
log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid());
log("sqlite3 namespace:",sqlite3);
try {
- [ demo1 ].forEach((f)=>f(sqlite3, Module))
+ demo1(sqlite3);
}catch(e){
error("Exception:",e.message);
throw e;
-C Improve\san\sexception\scheck\sin\sdemo-oo1.js.
-D 2022-08-18T11:16:27.283
+C javascript:\srename\sand\ssimplify\sDB.callInTransaction()\sas\sDB.transaction().\sAdd\sDB.savepoint(),\swhich\sworks\sthe\ssame\sas\stransaction()\sbut\suses\ssavepoints.\sCorrect\sconcatenation\sof\sarguments\sto\sSQLite3Error\sto\suse\sspaces\sinstead\sof\scommas.\sTest\sthat\sdemo-oo1.js\sworks\swith\spersistent\sOPFS\sstorage\s(output\sdiffers\sdue\sto\spersistent\srows,\sbut\sthe\sdemo\sworks).
+D 2022-08-18T12:21:58.613
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b
F ext/wasm/api/sqlite3-api-cleanup.js 149fd63a0400cd1d69548887ffde2ed89c13283384a63c2e9fcfc695e38a9e11
F ext/wasm/api/sqlite3-api-glue.js 4a09dd1153874f7a716cf953329bc1e78bf24e0870a9aad15214ffd51ac913bc
-F ext/wasm/api/sqlite3-api-oo1.js d68dc7a692bc54385d9b851e914b386a146dc6c86624bfeb4672280a2d822082
+F ext/wasm/api/sqlite3-api-oo1.js 6c14e97987fafdd5f63ffa8a086e5316d49c115743add4dabfacb302a7c76b45
F ext/wasm/api/sqlite3-api-opfs.js c93cdd14f81a26b3a64990515ee05c7e29827fbc8fba4e4c2fef3a37a984db89
F ext/wasm/api/sqlite3-api-prologue.js 96997e411b41ff3d4024c2ad625c5cdb7b6a619ba8beece4662ad190191b1119
F ext/wasm/api/sqlite3-api-worker1.js 74130ec4979baeaf3e909c7619b99e9f466e2d816cd07370987ff639d168ef45
F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0
F ext/wasm/common/whwasmutil.js 41b8e097e0a9cb07c24c0ede3c81b72470a63f4a4efb07f75586dc131569f5ae
F ext/wasm/demo-oo1.html 75646855b38405d82781246fd08c852a2b3bee05dd9f0fe10ab655a8cffb79aa
-F ext/wasm/demo-oo1.js 09c2e3f8dab87308d945da51eef4dae206f1e0a4edc6d2207096b5617a64b651
+F ext/wasm/demo-oo1.js 8be9c6be3c8e579eab4e7a5ee720ed122e38275a1f105169c6826193e42cf102
F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8
F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P f5059ee6f9fc55a381cbf08a30dfb9a5636c0b44341e42f4e9f12a3b109b5507
-R 26de05b21625601bc9d1934cb0717538
+P 55e1b775fa7ec492817cc847036df7794c3c22b78558e9e9c513f24c775dedab
+R c7dd436c51b79816d21badfd7965a0c6
U stephan
-Z 631a9c2307319184f6094a1100613815
+Z 56fa2cd60f3999351a57836a87a9c295
# Remove this line to create a well-formed Fossil manifest.
-55e1b775fa7ec492817cc847036df7794c3c22b78558e9e9c513f24c775dedab
\ No newline at end of file
+e8c323f12b6223bc9dcbbec40698969c7917128aa50cf599c247df23f607ae61
\ No newline at end of file