diff --git a/plugins/tiddlywiki/multiwikiserver/modules/sql-tiddler-database.js b/plugins/tiddlywiki/multiwikiserver/modules/sql-tiddler-database.js index f97e8dd0b..2a0df394a 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/sql-tiddler-database.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/sql-tiddler-database.js @@ -25,6 +25,7 @@ function SqlTiddlerDatabase(options) { } var databasePath = options.databasePath || ":memory:"; this.db = new $tw.sqlite3.Database(databasePath,{verbose: undefined && console.log}); + this.transactionDepth = 0; } SqlTiddlerDatabase.prototype.close = function() { @@ -452,6 +453,25 @@ SqlTiddlerDatabase.prototype.getRecipeBags = function(recipename) { return rows.map(value => value.bag_name); }; +/* +Execute the given function in a transaction, committing if successful but rolling back if an error occurs. Returns whatever the given function returns. + +Calls to this function can be safely nested, but only the top-most call will actually take place in a transaction. +*/ +SqlTiddlerDatabase.prototype.transaction = function(fn) { + try { + const alreadyInTransaction = this.transactionDepth > 0; + this.transactionDepth++; + if(alreadyInTransaction) { + return fn(); + } else { + return this.db.transaction(fn)(); + } + } finally { + this.transactionDepth--; + } +}; + exports.SqlTiddlerDatabase = SqlTiddlerDatabase; })(); \ No newline at end of file diff --git a/plugins/tiddlywiki/multiwikiserver/modules/sql-tiddler-store.js b/plugins/tiddlywiki/multiwikiserver/modules/sql-tiddler-store.js index 05b5e98c9..b9b0a0b54 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/sql-tiddler-store.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/sql-tiddler-store.js @@ -83,25 +83,28 @@ SqlTiddlerStore.prototype.saveEntityStateTiddler = function(tiddler) { }; SqlTiddlerStore.prototype.updateAdminWiki = function() { - // Update bags - for(const bagInfo of this.listBags()) { - this.saveEntityStateTiddler({ - title: "bags/" + bagInfo.bag_name, - "bag-name": bagInfo.bag_name, - text: bagInfo.description - }); - } - // Update recipes - for(const recipeInfo of this.listRecipes()) { - this.saveEntityStateTiddler({ - title: "recipes/" + recipeInfo.recipe_name, - "recipe-name": recipeInfo.recipe_name, - text: recipeInfo.description, - list: $tw.utils.stringifyList(this.getRecipeBags(recipeInfo.recipe_name).map(bag_name => { - return this.entityStateTiddlerPrefix + "bags/" + bag_name; - })) - }); - } + var self = this; + return this.sqlTiddlerDatabase.transaction(function() { + // Update bags + for(const bagInfo of self.listBags()) { + self.saveEntityStateTiddler({ + title: "bags/" + bagInfo.bag_name, + "bag-name": bagInfo.bag_name, + text: bagInfo.description + }); + } + // Update recipes + for(const recipeInfo of self.listRecipes()) { + self.saveEntityStateTiddler({ + title: "recipes/" + recipeInfo.recipe_name, + "recipe-name": recipeInfo.recipe_name, + text: recipeInfo.description, + list: $tw.utils.stringifyList(self.getRecipeBags(recipeInfo.recipe_name).map(bag_name => { + return self.entityStateTiddlerPrefix + "bags/" + bag_name; + })) + }); + } + }); }; /* @@ -135,17 +138,20 @@ SqlTiddlerStore.prototype.processCanonicalUriTiddler = function(tiddlerFields,ba SqlTiddlerStore.prototype.saveTiddlersFromPath = function(tiddler_files_path,bag_name) { - // Clear out the bag - this.deleteAllTiddlersInBag(bag_name); - // Get the tiddlers - var path = require("path"); - var tiddlersFromPath = $tw.loadTiddlersFromPath(path.resolve($tw.boot.corePath,$tw.config.editionsPath,tiddler_files_path)); - // Save the tiddlers - for(const tiddlersFromFile of tiddlersFromPath) { - for(const tiddler of tiddlersFromFile.tiddlers) { - this.saveBagTiddler(tiddler,bag_name); + var self = this; + this.sqlTiddlerDatabase.transaction(function() { + // Clear out the bag + self.deleteAllTiddlersInBag(bag_name); + // Get the tiddlers + var path = require("path"); + var tiddlersFromPath = $tw.loadTiddlersFromPath(path.resolve($tw.boot.corePath,$tw.config.editionsPath,tiddler_files_path)); + // Save the tiddlers + for(const tiddlersFromFile of tiddlersFromPath) { + for(const tiddler of tiddlersFromFile.tiddlers) { + self.saveBagTiddler(tiddler,bag_name); + } } - } + }); }; SqlTiddlerStore.prototype.logTables = function() { @@ -157,17 +163,20 @@ SqlTiddlerStore.prototype.listBags = function() { }; SqlTiddlerStore.prototype.createBag = function(bagname,description) { - const validationBagName = this.validateItemName(bagname); - if(validationBagName) { - return {message: validationBagName}; - } - this.sqlTiddlerDatabase.createBag(bagname,description); - this.saveEntityStateTiddler({ - title: "bags/" + bagname, - "bag-name": bagname, - text: description + var self = this; + return this.sqlTiddlerDatabase.transaction(function() { + const validationBagName = self.validateItemName(bagname); + if(validationBagName) { + return {message: validationBagName}; + } + self.sqlTiddlerDatabase.createBag(bagname,description); + self.saveEntityStateTiddler({ + title: "bags/" + bagname, + "bag-name": bagname, + text: description + }); + return null; }); - return null; }; SqlTiddlerStore.prototype.listRecipes = function() { @@ -191,16 +200,19 @@ SqlTiddlerStore.prototype.createRecipe = function(recipename,bagnames,descriptio if(bagnames.length === 0) { return {message: "Recipes must contain at least one bag"}; } - this.sqlTiddlerDatabase.createRecipe(recipename,bagnames,description); - this.saveEntityStateTiddler({ - title: "recipes/" + recipename, - "recipe-name": recipename, - text: description, - list: $tw.utils.stringifyList(bagnames.map(bag_name => { - return this.entityStateTiddlerPrefix + "bags/" + bag_name; - })) + var self = this; + return this.sqlTiddlerDatabase.transaction(function() { + self.sqlTiddlerDatabase.createRecipe(recipename,bagnames,description); + self.saveEntityStateTiddler({ + title: "recipes/" + recipename, + "recipe-name": recipename, + text: description, + list: $tw.utils.stringifyList(bagnames.map(bag_name => { + return self.entityStateTiddlerPrefix + "bags/" + bag_name; + })) + }); + return null; }); - return null; }; /* @@ -270,7 +282,10 @@ SqlTiddlerStore.prototype.getRecipeTiddlers = function(recipename) { }; SqlTiddlerStore.prototype.deleteAllTiddlersInBag = function(bagname) { - return this.sqlTiddlerDatabase.deleteAllTiddlersInBag(bagname); + var self = this; + return this.sqlTiddlerDatabase.transaction(function() { + return self.sqlTiddlerDatabase.deleteAllTiddlersInBag(bagname); + }); }; /*