From 68a89b615de240ef23d17127bd4433b7c3ae5ba6 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Fri, 5 Jan 2024 10:58:07 +0000 Subject: [PATCH] Use a persistent disk-based database --- .../multiwikiserver/modules/init.js | 18 ++++--- .../modules/sql-tiddler-store.js | 50 ++++++++++++++----- .../modules/tests-sql-tiddler-store.js | 11 ++-- 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/plugins/tiddlywiki/multiwikiserver/modules/init.js b/plugins/tiddlywiki/multiwikiserver/modules/init.js index 7dce59ef4..8b0624ece 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/init.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/init.js @@ -19,6 +19,7 @@ exports.before = ["story"]; exports.synchronous = true; exports.startup = function() { + var path = require("path"); // Install the sqlite3 global namespace $tw.sqlite3 = { Database: null @@ -33,16 +34,21 @@ exports.startup = function() { logger.alert("The plugin 'tiddlywiki/multiwikiserver' requires the better-sqlite3 npm package to be installed. Run 'npm install' in the root of the TiddlyWiki repository"); return; } + // Compute the database path + var databasePath = path.resolve($tw.boot.wikiPath,"database.sqlite"); // Create and initialise the tiddler store var SqlTiddlerStore = require("$:/plugins/tiddlywiki/multiwikiserver/sql-tiddler-store.js").SqlTiddlerStore; - $tw.sqlTiddlerStore = new SqlTiddlerStore({}); + $tw.sqlTiddlerStore = new SqlTiddlerStore({ + databasePath: databasePath + }); $tw.sqlTiddlerStore.createTables(); // Create bags and recipes - $tw.sqlTiddlerStore.saveBag("bag-alpha"); - $tw.sqlTiddlerStore.saveBag("bag-beta"); - $tw.sqlTiddlerStore.saveBag("bag-gamma"); - $tw.sqlTiddlerStore.saveRecipe("recipe-rho",["bag-alpha","bag-beta"]); - $tw.sqlTiddlerStore.saveRecipe("recipe-sigma",["bag-alpha","bag-gamma"]); + $tw.sqlTiddlerStore.createBag("bag-alpha"); + $tw.sqlTiddlerStore.createBag("bag-beta"); + $tw.sqlTiddlerStore.createBag("bag-gamma"); + $tw.sqlTiddlerStore.createRecipe("recipe-rho",["bag-alpha","bag-beta"]); + $tw.sqlTiddlerStore.createRecipe("recipe-sigma",["bag-alpha","bag-gamma"]); + $tw.sqlTiddlerStore.createRecipe("recipe-tau",["bag-alpha"]); // Save tiddlers $tw.sqlTiddlerStore.saveTiddler({title: "Another Tiddler",text: "I'm in alpha",tags: "one two three"},"bag-alpha"); $tw.sqlTiddlerStore.saveTiddler({title: "Hello There",text: "I'm in alpha as well",tags: "one two three"},"bag-alpha"); diff --git a/plugins/tiddlywiki/multiwikiserver/modules/sql-tiddler-store.js b/plugins/tiddlywiki/multiwikiserver/modules/sql-tiddler-store.js index de33b6c71..f6f6dc10f 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/sql-tiddler-store.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/sql-tiddler-store.js @@ -9,9 +9,16 @@ Functions to perform basic tiddler operations with a sqlite3 database (function() { +/* +Create a tiddler store. Options include: + +databasePath - path to the database file (can be ":memory:" to get a temporary database) +*/ function SqlTiddlerStore(options) { + options = options || {}; + var databasePath = options.databasePath || ":memory:"; // Create our database - this.db = new $tw.sqlite3.Database(":memory:",{verbose: undefined && console.log}); + this.db = new $tw.sqlite3.Database(databasePath,{verbose: undefined && console.log}); } SqlTiddlerStore.prototype.close = function() { @@ -103,34 +110,51 @@ SqlTiddlerStore.prototype.logTables = function() { } }; -SqlTiddlerStore.prototype.saveBag = function(bagname) { +SqlTiddlerStore.prototype.createBag = function(bagname) { // Run the queries this.runStatement(` - INSERT OR REPLACE INTO bags (bag_name, accesscontrol) VALUES ($bag_name, $accesscontrol) + INSERT OR IGNORE INTO bags (bag_name, accesscontrol) + VALUES ($bag_name, '') + `,{ + bag_name: bagname + }); + this.runStatement(` + UPDATE bags + SET accesscontrol = $accesscontrol + WHERE bag_name = $bag_name `,{ bag_name: bagname, accesscontrol: "[some access control stuff]" }); }; -SqlTiddlerStore.prototype.saveRecipe = function(recipename,bagnames) { +SqlTiddlerStore.prototype.createRecipe = function(recipename,bagnames) { // Run the queries this.runStatement(` - -- Insert or replace the recipe with the given name - INSERT OR REPLACE INTO recipes (recipe_name) + -- Create the entry in the recipes table if required + INSERT OR IGNORE INTO recipes (recipe_name) VALUES ($recipe_name) `,{ recipe_name: recipename }); this.runStatement(` - -- Insert bag names into recipe_bags for the given recipe name + -- Delete existing recipe_bags entries for this recipe + DELETE FROM recipe_bags WHERE recipe_id = (SELECT recipe_id FROM recipes WHERE recipe_name = $recipe_name) + `,{ + recipe_name: recipename + }); + console.log(this.runStatementGetAll(` + SELECT * FROM json_each($bag_names) AS bag + `,{ + bag_names: JSON.stringify(bagnames) + })); + this.runStatement(` INSERT INTO recipe_bags (recipe_id, bag_id, position) - SELECT r.recipe_id, b.bag_id, j.key - FROM ( - SELECT * FROM json_each($bag_names) - ) AS j - JOIN bags AS b ON b.bag_name = j.value - JOIN recipes AS r ON r.recipe_name = $recipe_name; + SELECT r.recipe_id, b.bag_id, j.key as position + FROM recipes r + JOIN bags b + LEFT JOIN json_each($bag_names) AS j ON j.value = b.bag_name + WHERE r.recipe_name = $recipe_name `,{ recipe_name: recipename, bag_names: JSON.stringify(bagnames) diff --git a/plugins/tiddlywiki/multiwikiserver/modules/tests-sql-tiddler-store.js b/plugins/tiddlywiki/multiwikiserver/modules/tests-sql-tiddler-store.js index abb77dba3..ed630a9a5 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/tests-sql-tiddler-store.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/tests-sql-tiddler-store.js @@ -20,11 +20,12 @@ describe("SQL tiddler store", function() { const sqlTiddlerStore = new SqlTiddlerStore({}); sqlTiddlerStore.createTables(); // Create bags and recipes - sqlTiddlerStore.saveBag("bag-alpha"); - sqlTiddlerStore.saveBag("bag-beta"); - sqlTiddlerStore.saveBag("bag-gamma"); - sqlTiddlerStore.saveRecipe("recipe-rho",["bag-alpha","bag-beta"]); - sqlTiddlerStore.saveRecipe("recipe-sigma",["bag-alpha","bag-gamma"]); + sqlTiddlerStore.createBag("bag-alpha"); + sqlTiddlerStore.createBag("bag-beta"); + sqlTiddlerStore.createBag("bag-gamma"); + sqlTiddlerStore.createRecipe("recipe-rho",["bag-gamma"]); + sqlTiddlerStore.createRecipe("recipe-rho",["bag-alpha","bag-beta"]); + sqlTiddlerStore.createRecipe("recipe-sigma",["bag-alpha","bag-gamma"]); // Tear down afterAll(function() { // Close the database