1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-09-30 07:50:47 +00:00

Use sql functions for processing shadow tiddlers

This commit is contained in:
jeremy@jermolene.com 2023-06-27 19:10:42 +01:00
parent 7e6072611e
commit 9cb8721343

View File

@ -13,82 +13,228 @@ This file is spliced into the HTML file to be executed before the boot kernel ha
$tw.Wiki = function(options) { $tw.Wiki = function(options) {
// Create a test database and store and retrieve some data // Create a test database and store and retrieve some data
var db = new $tw.sqlite3.oo1.DB("/tiddlywiki.sqlite3","c"); var db = new $tw.sqlite3.oo1.DB("/tiddlywiki.sqlite3","c");
db.exec({
sql:"CREATE TABLE IF NOT EXISTS t(a,b)"
});
db.exec({
sql: "insert into t(a,b) values (?,?)",
bind: [3, 1415926]
});
db.exec({
sql: "insert into t(a,b) values (?,?)",
bind: [1, 4142136]
});
let resultRows = [];
db.exec({
sql: "select a, b from t order by a limit 3",
rowMode: "object",
resultRows: resultRows
});
console.log("Result rows:",JSON.stringify(resultRows,undefined,2));
// Basic tiddler operations // Basic tiddler operations
db.exec({ db.exec({
sql: [ sql: `
"CREATE TABLE IF NOT EXISTS tiddlers (title TEXT PRIMARY KEY,meta TEXT,text TEXT);", DROP TABLE IF EXISTS tiddlers;
"CREATE INDEX tiddlers_title_index ON tiddlers(title);" CREATE TABLE tiddlers (
] title TEXT NOT NULL,
shadow INTEGER NOT NULL CHECK (shadow = 0 OR shadow = 1), -- 0=real tiddler, 1=shadow tiddler
shadowsource TEXT,
meta TEXT NOT NULL,
text TEXT NOT NULL,
PRIMARY KEY(title,shadow)
);
CREATE INDEX tiddlers_title_index ON tiddlers(title);
`
}); });
function sqlSaveTiddler(tiddlerFields) { $tw.stats = $tw.stats || {};
$tw.stats.countSaveTiddler = 0;
$tw.stats.countDeleteTiddler = 0;
$tw.stats.countGetTiddler = 0;
$tw.stats.countGetTiddlerSucceeeded = 0;
$tw.stats.countGetTiddlerFailed = 0;
$tw.stats.countGetShadowSource = 0;
$tw.stats.countAllTitles = 0;
$tw.stats.countAllShadowTitles = 0;
$tw.stats.failuresGetTiddler = {};
$tw.stats.sortFailures = function() {
return Object.keys($tw.stats.failuresGetTiddler).sort(function(a,b) {
return $tw.stats.failuresGetTiddler[a] - $tw.stats.failuresGetTiddler[b];
}).map(function(a) {
return {title: a, occurances: $tw.stats.failuresGetTiddler[a]};
});
};
/*
Save a tiddler. shadowSource should be falsy for ordinary tiddlers, or the source plugin title for shadow tiddlers
*/
function sqlSaveTiddler(tiddlerFields,shadowSource) {
$tw.stats.countSaveTiddler++;
db.exec({ db.exec({
sql: "replace into tiddlers(title,meta,text) values ($title,$meta,$text)", sql: "replace into tiddlers(title,shadow,shadowsource,meta,text) values ($title,$shadow,$shadowsource,$meta,$text)",
bind: { bind: {
$title: tiddlerFields.title, $title: tiddlerFields.title,
$shadow: shadowSource ? 1 : 0,
$shadowsource: shadowSource ? shadowSource : null,
$meta: JSON.stringify(Object.assign({},tiddlerFields,{title: undefined, text: undefined})), $meta: JSON.stringify(Object.assign({},tiddlerFields,{title: undefined, text: undefined})),
$text: tiddlerFields.text || "" $text: tiddlerFields.text || ""
} }
}); });
} }
function sqlDeleteTiddler(title) { function sqlDeleteTiddler(title) {
$tw.stats.countDeleteTiddler++;
db.exec({ db.exec({
sql: "delete from tiddlers where title = $title", sql: "delete from tiddlers where title = $title and shadow = 0",
bind: { bind: {
$title: title $title: title
} }
}); });
} }
function sqlGetTiddler(title) { function sqlClearShadows() {
let resultRows = [];
db.exec({ db.exec({
sql: "select title, meta, text from tiddlers where title = $title", sql: "delete from tiddlers where shadow = 1"
bind: {
$title: title
},
rowMode: "object",
resultRows: resultRows
}); });
if(resultRows.length > 0) { }
return Object.assign({},JSON.parse(resultRows[0].meta),{title: resultRows[0].title, text: resultRows[0].text}); var statementTiddlerExists = db.prepare("select title from tiddlers where title = $title and shadow = 0;")
function sqlTiddlerExists(title) {
statementTiddlerExists.bind({
$title: title
});
if(statementTiddlerExists.step()) {
statementTiddlerExists.reset();
return true;
} else { } else {
statementTiddlerExists.reset();
return false;
}
}
var statementGetTiddler = db.prepare("select title, shadow, meta, text from tiddlers where title = $title order by shadow");
function sqlGetTiddler(title) {
$tw.stats.countGetTiddler++;
statementGetTiddler.bind({
$title: title
});
if(statementGetTiddler.step()) {
$tw.stats.countGetTiddlerSucceeeded++;
var row = statementGetTiddler.get({});
statementGetTiddler.reset();
return Object.assign({},JSON.parse(row.meta),{title: row.title, text: row.text});
} else {
$tw.stats.countGetTiddlerFailed++;
$tw.stats.failuresGetTiddler[title] = ($tw.stats.failuresGetTiddler[title] || 0) + 1;
statementGetTiddler.reset();
return undefined; return undefined;
} }
} }
function sqlAllTitles() { var statementGetShadowSource = db.prepare("select title, shadowsource from tiddlers where title = $title and shadow = 1");
let resultRows = []; function sqlGetShadowSource(title) {
db.exec({ $tw.stats.countGetShadowSource++;
sql: "select title from tiddlers order by title", statementGetShadowSource.bind({
rowMode: "object", $title: title
resultRows: resultRows
});
return resultRows.map(row => {
return row.title;
}); });
if(statementGetShadowSource.step()) {
var row = statementGetShadowSource.get({});
statementGetShadowSource.reset();
return row.shadowsource;
} else {
statementGetShadowSource.reset();
return undefined;
}
}
var statementAllTitles = db.prepare("select title from tiddlers where shadow = 0 order by title");
function sqlAllTitles() {
$tw.stats.countAllTitles++;
let resultRows = [];
while(statementAllTitles.step()) {
var row = statementAllTitles.get({});
resultRows.push(row.title);
}
statementAllTitles.reset();
return resultRows;
}
var statementAllShadowTitles = db.prepare("select title from tiddlers where shadow = 1 order by title");
function sqlAllShadowTitles() {
$tw.stats.countAllShadowTitles++;
let resultRows = [];
while(statementAllShadowTitles.step()) {
var row = statementAllShadowTitles.get({});
resultRows.push(row.title);
}
statementAllShadowTitles.reset();
return resultRows;
}
var statementEachTiddler = db.prepare("select title, meta, text from tiddlers where shadow = 0 order by title");
function sqlEachTiddler(callback) {
while(statementEachTiddler.step()) {
var row = statementEachTiddler.get({}),
tiddlerFields = Object.assign({},JSON.parse(row.meta),{title: row.title, text: row.text});
callback(tiddlerFields,row.title);
}
statementEachTiddler.reset();
}
/*
We get all the rows where either the shadow field is 1 and there is no row with the same title and
a shadow field value of zero, or the shadow field is zero and there also exists a row with the same
title and a shadow field value of 1
*/
var statementEachShadowTiddler = db.prepare(`
select title, meta, text
from tiddlers t1
where t1.shadow = 1
and not exists (
select 1
from tiddlers t2
where t2.title = t1.title
and t2.shadow = 0
)
union
select title, meta, text
from tiddlers t3
where t3.shadow = 0
and exists (
select 1
from tiddlers t4
where t4.title = t3.title
and t4.shadow = 1
);
order by title;
`);
function sqlEachShadowTiddler(callback) {
while(statementEachShadowTiddler.step()) {
var row = statementEachShadowTiddler.get({});
var tiddlerFields = Object.assign({},JSON.parse(row.meta),{title: row.title, text: row.text});
callback(tiddlerFields,row.title);
}
statementEachShadowTiddler.reset();
}
/*
Return all the tiddler rows that have the "shadow" field set to 1, but only where the "title"
field doesn't appear in a row with the "shadow" field set to 0
*/
var statementEachTiddlerPlusShadows = db.prepare(`
select title,meta,text from tiddlers t1
where t1.shadow = 1
and not exists (
select 1
from tiddlers t2
where t2.title = t1.title
and t2.shadow = 0
)
order by t1.title;
`);
function sqlEachTiddlerPlusShadows(callback) {
sqlEachTiddler(callback);
while(statementEachTiddlerPlusShadows.step()) {
var row = statementEachTiddlerPlusShadows.get({});
var tiddlerFields = Object.assign({},JSON.parse(row.meta),{title: row.title, text: row.text});
callback(tiddlerFields,row.title);
}
statementEachTiddlerPlusShadows.reset();
}
/*
Return all rows where the shadow field is zero, and there is no row with the same title and a shadow field of 1
*/
var statementEachShadowPlusTiddlers = db.prepare(`
select title,meta,text from tiddlers t1
where t1.shadow = 0
and not exists (
select 1
from tiddlers t2
where t2.title = t1.title
and t2.shadow = 1
)
order by t1.title;
`);
function sqlEachShadowPlusTiddlers(callback) {
sqlEachShadowTiddler(callback);
while(statementEachShadowPlusTiddlers.step()) {
var row = statementEachShadowPlusTiddlers.get({});
var tiddlerFields = Object.assign({},JSON.parse(row.meta),{title: row.title, text: row.text});
callback(tiddlerFields,row.title);
}
statementEachShadowPlusTiddlers.reset();
} }
sqlSaveTiddler({title: "HelloThere", text: "One"});
console.log(sqlGetTiddler("HelloThere"));
sqlSaveTiddler({title: "HelloThere", text: "Two", custom: "A custom field"});
console.log(sqlGetTiddler("HelloThere"));
sqlSaveTiddler({title: "AnotherTiddler", text: "Three"});
console.log(sqlAllTitles());
// Plain JS wiki store implementation follows // Plain JS wiki store implementation follows
options = options || {}; options = options || {};
var self = this, var self = this,
@ -97,9 +243,8 @@ $tw.Wiki = function(options) {
}, },
pluginTiddlers = [], // Array of tiddlers containing registered plugins, ordered by priority pluginTiddlers = [], // Array of tiddlers containing registered plugins, ordered by priority
pluginInfo = Object.create(null), // Hashmap of parsed plugin content pluginInfo = Object.create(null), // Hashmap of parsed plugin content
shadowTiddlers = Object.create(null), // Hashmap by title of {source:, tiddler:}
getShadowTiddlerTitles = function() { getShadowTiddlerTitles = function() {
return Object.keys(shadowTiddlers); return sqlAllShadowTitles();
}; };
// $tw.utils replacements // $tw.utils replacements
var eachObj = function(object,callback) { var eachObj = function(object,callback) {
@ -204,11 +349,6 @@ $tw.Wiki = function(options) {
var t = sqlGetTiddler(title); var t = sqlGetTiddler(title);
if(t !== undefined) { if(t !== undefined) {
return new $tw.Tiddler(t); return new $tw.Tiddler(t);
} else {
var s = shadowTiddlers[title];
if(s !== undefined) {
return s.tiddler;
}
} }
} }
return undefined; return undefined;
@ -221,12 +361,9 @@ $tw.Wiki = function(options) {
// Iterate through all tiddler titles // Iterate through all tiddler titles
this.each = function(callback) { this.each = function(callback) {
var titles = getTiddlerTitles(), sqlEachTiddler(function(tiddlerFields,title) {
index,titlesLength,title; callback(new $tw.Tiddler(tiddlerFields),title);
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) { });
title = titles[index];
callback(self.getTiddler(title),title);
}
}; };
// Get an array of all shadow tiddler titles // Get an array of all shadow tiddler titles
@ -236,72 +373,35 @@ $tw.Wiki = function(options) {
// Iterate through all shadow tiddler titles // Iterate through all shadow tiddler titles
this.eachShadow = function(callback) { this.eachShadow = function(callback) {
var titles = getShadowTiddlerTitles(), sqlEachShadowTiddler(function(tiddlerFields,title) {
index,titlesLength,title; callback(new $tw.Tiddler(tiddlerFields),title);
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) { });
title = titles[index];
if(self.tiddlerExists(title)) {
callback(self.getTiddler(title),title);
} else {
var shadowInfo = shadowTiddlers[title];
callback(shadowInfo.tiddler,title);
}
}
}; };
// Iterate through all tiddlers and then the shadows // Iterate through all tiddlers and then the shadows
this.eachTiddlerPlusShadows = function(callback) { this.eachTiddlerPlusShadows = function(callback) {
var index,titlesLength,title, sqlEachTiddlerPlusShadows(function(tiddlerFields,title) {
titles = getTiddlerTitles(); callback(new $tw.Tiddler(tiddlerFields),title);
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) { });
title = titles[index];
callback(self.getTiddler(title),title);
}
titles = getShadowTiddlerTitles();
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
title = titles[index];
if(!self.tiddlerExists(title)) {
var shadowInfo = shadowTiddlers[title];
callback(shadowInfo.tiddler,title);
}
}
}; };
// Iterate through all the shadows and then the tiddlers // Iterate through all the shadows and then the tiddlers
this.eachShadowPlusTiddlers = function(callback) { this.eachShadowPlusTiddlers = function(callback) {
var index,titlesLength,title, sqlEachShadowPlusTiddlers(function(tiddlerFields,title) {
titles = getShadowTiddlerTitles(); callback(new $tw.Tiddler(tiddlerFields),title);
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) { });
title = titles[index];
if(self.tiddlerExists(title)) {
callback(self.getTiddler(title),title);
} else {
var shadowInfo = shadowTiddlers[title];
callback(shadowInfo.tiddler,title);
}
}
titles = getTiddlerTitles();
for(index = 0, titlesLength = titles.length; index < titlesLength; index++) {
title = titles[index];
if(!shadowTiddlers[title]) {
callback(self.getTiddler(title),title);
}
}
}; };
this.tiddlerExists = function(title) { this.tiddlerExists = function(title) {
return !!sqlGetTiddler(title); return sqlTiddlerExists(title);
}; };
this.isShadowTiddler = function(title) { this.isShadowTiddler = function(title) {
return hop(shadowTiddlers,title); return !!sqlGetShadowSource(title);
}; };
this.getShadowSource = function(title) { this.getShadowSource = function(title) {
if(hop(shadowTiddlers,title)) { return sqlGetShadowSource(title);
return shadowTiddlers[title].source;
}
return null;
}; };
// Get an array of all the currently recognised plugin types // Get an array of all the currently recognised plugin types
@ -405,22 +505,19 @@ $tw.Wiki = function(options) {
} }
}); });
// Now go through the plugins in ascending order and assign the shadows // Now go through the plugins in ascending order and assign the shadows
shadowTiddlers = Object.create(null); sqlClearShadows();
eachObj(pluginTiddlers,function(tiddler) { eachObj(pluginTiddlers,function(tiddler) {
// Extract the constituent tiddlers // Extract the constituent tiddlers
if(hop(pluginInfo,tiddler.fields.title)) { if(hop(pluginInfo,tiddler.fields.title)) {
eachObj(pluginInfo[tiddler.fields.title].tiddlers,function(constituentTiddler,constituentTitle) { eachObj(pluginInfo[tiddler.fields.title].tiddlers,function(constituentTiddler,constituentTitle) {
// Save the tiddler object // Save the tiddler object
if(constituentTitle) { if(constituentTitle) {
shadowTiddlers[constituentTitle] = { var shadowTiddler = Object.assign({},constituentTiddler,{title: constituentTitle})
source: tiddler.fields.title, sqlSaveTiddler(shadowTiddler,tiddler.fields.title);
tiddler: new $tw.Tiddler(constituentTiddler,{title: constituentTitle})
};
} }
}); });
} }
}); });
shadowTiddlerTitles = null;
this.clearCache(null); this.clearCache(null);
this.clearGlobalCache(); this.clearGlobalCache();
}; };