1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-09-27 14:48:19 +00:00

Simplify the SQL schema

And introduce some very simple tests
This commit is contained in:
Jeremy Ruston 2023-10-21 12:00:51 +01:00
parent 1754be279f
commit c52014c66f
5 changed files with 429 additions and 214 deletions

View File

@ -54,6 +54,10 @@ self.sqlite3InitModule().then((sqlite3)=>{
$tw.sqlConsole = new $tw.SqlConsole();
// Get version numbers
console.log("sqlite3 version",capi.sqlite3_libversion());
// Run tests
if($tw.testSqlFunctions) {
$tw.testSqlFunctions();
}
// Boot TiddlyWiki
$tw.boot.boot();
});

View File

@ -7,5 +7,6 @@ tags: $:/tags/RawMarkupWikified
<script>`
{{$:/plugins/tiddlywiki/sqlite3store/suppress-boot.js}}
{{$:/plugins/tiddlywiki/sqlite3store/sql-functions.js}}
{{$:/plugins/tiddlywiki/sqlite3store/test-sql-functions.js}}
{{$:/plugins/tiddlywiki/sqlite3store/sql-wiki-store.js}}
`</script>`

View File

@ -43,103 +43,191 @@ $tw.SqlFunctions = function(options) {
*/
self.db.exec({
sql: `
DROP TABLE IF EXISTS plugins;
CREATE TABLE plugins (
plugintitle TEXT NOT NULL,
priority INTEGER NOT NULL,
PRIMARY KEY(plugintitle)
);
DROP TABLE IF EXISTS tiddlers;
CREATE TABLE tiddlers (
title TEXT NOT NULL ${COLLATION_CLAUSE},
shadow INTEGER NOT NULL CHECK (shadow = 0 OR shadow = 1), -- 0=real tiddler, 1=shadow tiddler
shadowsource TEXT,
plugintitle TEXT, -- or NULL for non-shadow tiddlers
meta TEXT NOT NULL,
text TEXT NOT NULL,
PRIMARY KEY(title,shadow)
PRIMARY KEY(title,plugintitle)
);
CREATE INDEX tiddlers_title_index ON tiddlers(title);
DROP TABLE IF EXISTS tags;
CREATE TABLE tags (
tag_id INTEGER PRIMARY KEY,
tag TEXT NOT NULL
);
DROP TABLE IF EXISTS tiddler_tags;
CREATE TABLE tiddler_tags (
tiddler_title TEXT NOT NULL,
tiddler_shadow INTEGER NOT NULL,
tag_id INTEGER NOT NULL,
FOREIGN KEY (tiddler_title, tiddler_shadow) REFERENCES tiddlers (title, shadow) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (tag_id) REFERENCES tags (tag_id) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY (tiddler_title, tiddler_shadow, tag_id)
DROP TABLE IF EXISTS titles;
CREATE TABLE titles (
title TEXT NOT NULL ${COLLATION_CLAUSE},
plugintitle TEXT, -- or NULL for non-shadow tiddlers
PRIMARY KEY(title)
);
`
});
/*
Save a tiddler. shadowSource should be falsy for ordinary tiddlers, or the source plugin title for shadow tiddlers
Debugging
*/
var statementLogTiddlersTable = self.db.prepare("select title, plugintitle, meta, text from tiddlers order by title, plugintitle;"),
statementLogPluginsTable = self.db.prepare("select plugintitle, priority from plugins order by plugintitle;"),
statementLogTitlesTable = self.db.prepare("select title, plugintitle from titles order by title;");
function sqlLogTable(statement) {
let resultRows = [];
while(statement.step()) {
var row = statement.get({});
resultRows.push(row);
}
statement.reset();
return resultRows;
}
this.sqlLogTables = function() {
console.log("tiddlers",sqlLogTable(statementLogTiddlersTable));
console.log("plugins",sqlLogTable(statementLogPluginsTable));
console.log("titles",sqlLogTable(statementLogTitlesTable));
};
/*
Set the plugin priorities
*/
this.sqlSetPluginPriorities = function(prioritisedPluginTitles) {
self.db.exec({
sql: "DELETE FROM plugins"
});
let priority = 1;
for(const plugintitle of prioritisedPluginTitles) {
self.db.exec({
sql: "insert or replace into plugins (plugintitle, priority) values ($plugintitle, $priority)",
bind: {
$plugintitle: plugintitle,
$priority: priority++
}
});
}
};
/*
Save a tiddler
*/
var querySaveTiddlerTableTiddlers = self.db.prepare(`
-- Insert the new tiddler into the tiddlers table
INSERT OR REPLACE INTO tiddlers (title, shadow, shadowsource, meta, text)
VALUES ($title, $shadow, $shadowsource, $meta, $text);
INSERT OR REPLACE INTO tiddlers (title, plugintitle, meta, text)
VALUES ($title, $plugintitle, $meta, $text);
`);
var querySaveTiddlerTableTags = self.db.prepare(`
-- Parse and insert tags from the $tags JSON array
WITH tag_values AS (
SELECT json_each.value AS tag
FROM json_each($tags)
)
INSERT INTO tags (tag)
SELECT DISTINCT tag
FROM tag_values
WHERE tag NOT IN (
SELECT tag
FROM tags
);
var querySaveTiddlerTableTitles = self.db.prepare(`
-- Insert the new title into the titles table
INSERT OR REPLACE INTO titles (title, plugintitle)
SELECT
t.title,
CASE
WHEN t.null_count > 0 THEN NULL
ELSE t.max_priority_plugintitle
END AS plugintitle
FROM (
SELECT
t.title,
SUM(CASE WHEN t.plugintitle IS NULL THEN 1 ELSE 0 END) AS null_count,
MAX(p.priority) AS max_priority,
FIRST_VALUE(t.plugintitle) OVER (PARTITION BY t.title ORDER BY p.priority DESC) AS max_priority_plugintitle
FROM tiddlers t
LEFT JOIN plugins p ON t.plugintitle = p.plugintitle
WHERE t.title = $title
GROUP BY t.title
) AS t;
`);
var querySaveTiddlerTableTiddlerTags = self.db.prepare(`
-- Associate the new tiddler with the tags in the tiddler_tags table
WITH tag_values AS (
SELECT json_each.value AS tag
FROM json_each($tags)
)
INSERT OR IGNORE INTO tiddler_tags (tiddler_title, tiddler_shadow, tag_id)
SELECT $title, $shadow, tags.tag_id
FROM tag_values
JOIN tags ON tag_values.tag = tags.tag;
`);
this.sqlSaveTiddler = function(tiddlerFields,shadowSource) {
var tags = JSON.stringify($tw.utils.parseStringArray(tiddlerFields.tags) || []);
this.sqlSaveTiddler = function(tiddlerFields,plugintitle) {
querySaveTiddlerTableTiddlers.bind({
$title: tiddlerFields.title,
$shadow: shadowSource ? 1 : 0,
$shadowsource: shadowSource ? shadowSource : null,
$plugintitle: plugintitle || null,
$meta: JSON.stringify(Object.assign({},tiddlerFields,{title: undefined, text: undefined})),
$text: tiddlerFields.text || ""
});
querySaveTiddlerTableTiddlers.step();
querySaveTiddlerTableTiddlers.reset();
querySaveTiddlerTableTags.bind({
$tags: tags
});
querySaveTiddlerTableTags.step();
querySaveTiddlerTableTags.reset();
querySaveTiddlerTableTiddlerTags.bind({
querySaveTiddlerTableTitles.bind({
$title: tiddlerFields.title,
$shadow: shadowSource ? 1 : 0,
$tags: tags
});
querySaveTiddlerTableTiddlerTags.step();
querySaveTiddlerTableTiddlerTags.reset();
querySaveTiddlerTableTitles.step();
querySaveTiddlerTableTitles.reset();
};
/*
Delete a tiddler
*/
var statementDeleteTiddlerDeleteFromTiddlers = self.db.prepare(`
DELETE FROM tiddlers
WHERE title = $title AND plugintitle IS NULL;
`);
var statementDeleteTiddlerFindShadow = self.db.prepare(`
WITH HighestPriority AS (
SELECT
plugintitle,
MAX(priority) AS max_priority
FROM
plugins
WHERE
plugintitle IS NOT NULL
GROUP BY
plugintitle
)
SELECT
t.title,
t.plugintitle,
t.meta,
t.text
FROM
tiddlers AS t
JOIN
HighestPriority AS ho ON t.plugintitle = ho.plugintitle
WHERE
t.title = $title;
`);
this.sqlDeleteTiddler = function(title) {
self.db.exec({
sql: "delete from tiddlers where title = $title and shadow = 0",
bind: {
$title: title
}
// Delete the tiddler from the tiddlers table
statementDeleteTiddlerDeleteFromTiddlers.bind({
$title: title
});
statementDeleteTiddlerDeleteFromTiddlers.step();
statementDeleteTiddlerDeleteFromTiddlers.reset();
// Find any corresponding shadow tiddler
statementDeleteTiddlerFindShadow.bind({
$title: title
});
if(statementDeleteTiddlerFindShadow.step()) {
// There is a corresponding shadow
var row = statementDeleteTiddlerFindShadow.get({});
// Replace the tiddler with the shadow
self.db.exec({
sql: "insert or replace into titles (title, plugintitle) values ($title, $plugintitle)",
bind: {
$title: title,
$plugintitle: row.plugintitle
}
});
} else {
// There is no corresponding shadow
// Delete the tiddler
self.db.exec({
sql: "delete from titles where title = $title",
bind: {
$title: title
}
});
}
statementDeleteTiddlerFindShadow.reset();
};
/*
Remove all shadow tiddlers
*/
this.sqlClearShadows = function() {
self.db.exec({
sql: "delete from tiddlers where shadow = 1"
sql: "delete from tiddlers where plugintitle is not null"
});
self.db.exec({
sql: "delete from titles where plugintitle is not null"
});
};
var statementTiddlerExists = self.db.prepare(`select title from tiddlers where title = $title and shadow = 0;`)
/*
Check whether a tiddler exists
*/
var statementTiddlerExists = self.db.prepare(`select title from titles where title = $title and plugintitle is null;`)
this.sqlTiddlerExists = function(title) {
statementTiddlerExists.bind({
$title: title
@ -152,7 +240,16 @@ $tw.SqlFunctions = function(options) {
return false;
}
};
var statementGetTiddler = self.db.prepare(`select title, shadow, meta, text from tiddlers where title = $title order by shadow`);
/*
Get the value of a tiddler
*/
var statementGetTiddler = self.db.prepare(`
select t.title, ti.meta, ti.text
FROM titles AS t
JOIN tiddlers AS ti
ON t.title = ti.title AND (t.plugintitle = ti.plugintitle OR (t.plugintitle IS NULL AND ti.plugintitle IS NULL))
WHERE t.title = $title;
`);
this.sqlGetTiddler = function(title) {
statementGetTiddler.bind({
$title: title
@ -166,7 +263,30 @@ $tw.SqlFunctions = function(options) {
return undefined;
}
};
var statementGetShadowSource = self.db.prepare(`select title, shadowsource from tiddlers where title = $title and shadow = 1`);
/*
Get the plugin from which a tiddler came
*/
var statementGetShadowSource = self.db.prepare(`
WITH HighestPriority AS (
SELECT
plugintitle,
MAX(priority) AS max_priority
FROM
plugins
WHERE
plugintitle IS NOT NULL
GROUP BY
plugintitle
)
SELECT
t.plugintitle
FROM
tiddlers AS t
JOIN
HighestPriority AS ho ON t.plugintitle = ho.plugintitle
WHERE
t.title = $title;
`);
this.sqlGetShadowSource = function(title) {
statementGetShadowSource.bind({
$title: title
@ -174,13 +294,16 @@ $tw.SqlFunctions = function(options) {
if(statementGetShadowSource.step()) {
var row = statementGetShadowSource.get({});
statementGetShadowSource.reset();
return row.shadowsource;
return row.plugintitle;
} else {
statementGetShadowSource.reset();
return undefined;
return null;
}
};
var statementAllTitles = self.db.prepare(`select title from tiddlers where shadow = 0 order by title ${COLLATION_CLAUSE}`);
/*
Get all titles
*/
var statementAllTitles = self.db.prepare(`select title from titles order by title ${COLLATION_CLAUSE}`);
this.sqlAllTitles = function() {
let resultRows = [];
while(statementAllTitles.step()) {
@ -190,7 +313,15 @@ $tw.SqlFunctions = function(options) {
statementAllTitles.reset();
return resultRows;
};
var statementAllShadowTitles = self.db.prepare(`select title from tiddlers where shadow = 1 order by title ${COLLATION_CLAUSE}`);
/*
All shadow titles
*/
var statementAllShadowTitles = self.db.prepare(`
SELECT title
FROM tiddlers
WHERE plugintitle IS NOT NULL
ORDER BY title ${COLLATION_CLAUSE}
`);
this.sqlAllShadowTitles = function() {
let resultRows = [];
while(statementAllShadowTitles.step()) {
@ -200,7 +331,16 @@ $tw.SqlFunctions = function(options) {
statementAllShadowTitles.reset();
return resultRows;
};
var statementEachTiddler = self.db.prepare(`select title, meta, text from tiddlers where shadow = 0 order by title ${COLLATION_CLAUSE}`);
/*
Iterate through each tiddler
*/
var statementEachTiddler = self.db.prepare(`
SELECT t.title, ti.meta, ti.text
FROM titles AS t
LEFT JOIN tiddlers AS ti ON t.title = ti.title AND t.plugintitle IS NULL AND ti.plugintitle IS NULL
WHERE t.plugintitle IS NULL
ORDER BY t.title ${COLLATION_CLAUSE}
`);
this.sqlEachTiddler = function(callback) {
while(statementEachTiddler.step()) {
var row = statementEachTiddler.get({}),
@ -210,31 +350,14 @@ $tw.SqlFunctions = function(options) {
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
Iterate through each tiddler that is a shadow (including overridden shadows)
*/
var statementEachShadowTiddler = self.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 ${COLLATION_CLAUSE};
SELECT DISTINCT t.title, td.meta, td.text
FROM titles AS t
JOIN tiddlers AS td ON t.title = td.title
WHERE td.plugintitle IS NOT NULL
ORDER BY t.title ${COLLATION_CLAUSE};
`);
this.sqlEachShadowTiddler = function(callback) {
while(statementEachShadowTiddler.step()) {
@ -245,128 +368,47 @@ $tw.SqlFunctions = function(options) {
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
Iterate all tiddlers, and then the shadows
*/
var statementEachTiddlerPlusShadows = self.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 ${COLLATION_CLAUSE};
`);
this.sqlEachTiddlerPlusShadows = function(callback) {
self.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 = self.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 ${COLLATION_CLAUSE};
`);
this.sqlEachShadowPlusTiddlers = function(callback) {
self.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();
};
/*
Return all tiddlers with a given tag. Method determines the default ordering (before the list ordering fields are observed):
each: just ordinary tiddlers
eachShadow: just shadow tiddlers
*/
var statementGetTiddlersWithTag = self.db.prepare(`
SELECT t.title
FROM tiddlers AS t
JOIN tiddler_tags AS tt ON t.title = tt.tiddler_title AND t.shadow = tt.tiddler_shadow
WHERE tt.tag_id = (SELECT tag_id FROM tags WHERE tag = $tag)
AND (t.shadow = 0 OR NOT EXISTS (SELECT 1 FROM tiddlers WHERE title = t.title AND shadow = 0))
GROUP BY t.title, t.shadow
ORDER BY t.title ${COLLATION_CLAUSE} ASC, t.shadow ASC;
`);
this.sqlGetTiddlersWithTag = function(tag,method) {
statementGetTiddlersWithTag.bind({
$tag: tag
const titles = Object.create(null);
self.sqlEachTiddler(function(fields,title) {
titles[title] = true;
callback(fields,title);
});
var resultRows = [];
while(statementGetTiddlersWithTag.step()) {
var row = statementGetTiddlersWithTag.get({});
resultRows.push(row.title);
}
statementGetTiddlersWithTag.reset();
return resultRows;
};
/*
An optimisation of the filter [all[shadows+tiddlers]prefix[$:/language/Docs/Types/]get[name]length[]maxall[]]
*/
var statementQuickFilterAllShadowsTiddlersPrefixDocTypeMaxLength = self.db.prepare(`
SELECT MAX(LENGTH(name)) AS max_length
FROM (
SELECT title, shadow, JSON_EXTRACT(meta, '$.name') AS name
FROM tiddlers
WHERE title LIKE '$:/language/Docs/Types/%'
AND (shadow = 0 OR (shadow = 1 AND NOT EXISTS (
SELECT 1
FROM tiddlers AS t2
WHERE t2.title = tiddlers.title
AND t2.shadow = 0
)))
);
`);
this.sqlQuickFilterAllShadowsTiddlersPrefixDocTypeMaxLength = function() {
// We return a filter operation function that actually performs the query
return function(results,source,widget) {
var result = 0;
if(statementQuickFilterAllShadowsTiddlersPrefixDocTypeMaxLength.step()) {
var row = statementQuickFilterAllShadowsTiddlersPrefixDocTypeMaxLength.get({});
result = row.max_length;
self.sqlEachShadowTiddler(function(fields,title) {
if(!titles[title]) {
callback(fields,title);
}
statementQuickFilterAllShadowsTiddlersPrefixDocTypeMaxLength.reset();
results.clear();
results.push(result.toString());
};
});
};
/*
Debugging
Iterate all shadows, and then the tiddlers
*/
var statementLogTiddlerTable = self.db.prepare("select title, shadow, meta, text from tiddlers order by title,shadow;"),
statementLogTagsTable = self.db.prepare("select tag_id, tag from tags order by tag;"),
statementLogTiddlerTagsTable = self.db.prepare("select tiddler_title, tiddler_shadow, tag_id from tiddler_tags order by tiddler_title, tiddler_shadow;");
function sqlLogTable(statement) {
let resultRows = [];
while(statement.step()) {
var row = statement.get({});
resultRows.push(row);
}
statement.reset();
return resultRows;
}
this.sqlLogTables = function() {
console.log("tiddlers",sqlLogTable(statementLogTiddlerTable));
console.log("tags",sqlLogTable(statementLogTagsTable));
console.log("tiddler tags",sqlLogTable(statementLogTiddlerTagsTable));
this.sqlEachShadowPlusTiddlers = function(callback) {
const titles = Object.create(null);
self.sqlEachShadowTiddler(function(fields,title) {
titles[title] = true;
callback(fields,title);
});
self.sqlEachTiddler(function(fields,title) {
if(!titles[title]) {
callback(fields,title);
}
});
};
/*
Return all tiddlers with a given tag
*/
this.sqlGetTiddlersWithTag = function(tag,method) {
const titles = [];
self.sqlEachShadowPlusTiddlers(function(fields,title) {
var tags = $tw.utils.parseStringArray(fields.tags || "");
if(tags.indexOf(tag) !== -1) {
titles.push(title);
}
});
return titles;
};
};

View File

@ -352,6 +352,7 @@ $tw.Wiki = function(options) {
});
// Now go through the plugins in ascending order and assign the shadows
self.sqlFunctions.sqlClearShadows();
self.sqlFunctions.sqlSetPluginPriorities(pluginTiddlers.map(tiddler => tiddler.fields.title));
eachObj(pluginTiddlers,function(tiddler) {
// Extract the constituent tiddlers
if(hop(pluginInfo,tiddler.fields.title)) {
@ -372,11 +373,11 @@ $tw.Wiki = function(options) {
};
this.optimiseFilter = function(filterString) {
switch($tw.utils.trim(filterString)) {
case "[all[shadows+tiddlers]prefix[$:/language/Docs/Types/]get[name]length[]maxall[]]":
return [this.sqlFunctions.sqlQuickFilterAllShadowsTiddlersPrefixDocTypeMaxLength()];
break;
}
// switch($tw.utils.trim(filterString)) {
// case "[all[shadows+tiddlers]prefix[$:/language/Docs/Types/]get[name]length[]maxall[]]":
// return [this.sqlFunctions.sqlQuickFilterAllShadowsTiddlersPrefixDocTypeMaxLength()];
// break;
// }
return undefined;
};

View File

@ -0,0 +1,167 @@
/*\
title: $:/plugins/tiddlywiki/sqlite3store/test-sql-functions.js
type: application/javascript
Test harness for the functions in sql-functions.js
\*/
$tw.testSqlFunctions = function() {
// Deep equal
function deepEqual(obj1, obj2) {
if (obj1 === undefined && obj2 === undefined) {
return true;
}
if (obj1 === undefined || obj2 === undefined) {
return false;
}
if (obj1 === obj2) {
return true;
}
if (isPrimitive(obj1) && isPrimitive(obj2)) {
return obj1 === obj2;
}
if (Object.keys(obj1).length !== Object.keys(obj2).length) {
return false;
}
for (let key in obj1) {
if (!(key in obj2)) {
return false;
}
if (!deepEqual(obj1[key], obj2[key])) {
return false;
}
}
return true;
}
function isPrimitive(obj) {
return (obj !== Object(obj));
}
let tests = [];
// Define a test
function test(name, fn) {
tests.push({
name: name,
fn: fn
});
}
// Run all the tests
function run() {
while(tests.length > 0) {
const test = tests.shift();
try {
test.fn();
console.log("✅", test.name);
} catch (e) {
console.log("❌", test.name);
console.log(e.stack);
}
}
}
let assert = {
equal: function(obj1,obj2,message) {
if(!deepEqual(obj1,obj2)) {
throw new Error("" + (message || "assert.equal failed"));
}
}
}
// Define the tests
test("Instantiating the database", function () {
const sqlFunctions = new $tw.SqlFunctions();
test("Write a tiddler and retrieve it", function() {
// Utilities
function checkExists(title,result,message) {
const exists = sqlFunctions.sqlTiddlerExists(title);
assert.equal(exists,result,message);
}
function checkTiddler(title,result,message) {
const tiddler = sqlFunctions.sqlGetTiddler(title);
assert.equal(tiddler,result,message);
}
function checkShadowSource(title,result,message) {
const tiddler = sqlFunctions.sqlGetShadowSource(title);
assert.equal(tiddler,result,message);
}
function checkAllTitles(result,message) {
const titles = sqlFunctions.sqlAllTitles();
assert.equal(titles,result,message + " (sqlAllTitles)");
const accumulator = [];
sqlFunctions.sqlEachTiddler(function(tiddlerFields,title) {
accumulator.push(title);
});
assert.equal(accumulator,result,message + " (sqlEachTiddler)");
}
function checkAllShadowTitles(result,message) {
const titles = sqlFunctions.sqlAllShadowTitles();
assert.equal(titles,result,message);
}
// Save and verify an ordinary tiddler
sqlFunctions.sqlSaveTiddler({
title: "HelloThere",
text: "This is a tiddler"
});
checkExists("HelloThere",true,"Check the tiddler exists");
checkTiddler("HelloThere",{
title: "HelloThere",
text: "This is a tiddler"
},"Retrieve the tiddler");
checkShadowSource("HelloThere",null,"Check that the shadow source is correct");
// Delete the tiddler and check it no longer exists
sqlFunctions.sqlDeleteTiddler("HelloThere");
checkTiddler("HelloThere",undefined,"Try to retrieve the deleted tiddler");
checkExists("HelloThere",false,"Check the tiddler doesn't exist");
checkAllTitles([],"Check that the title list is correct");
checkAllShadowTitles([],"Check that the shadow title list is correct");
// Save and verify a shadow tiddler
sqlFunctions.sqlSetPluginPriorities(["myplugin"]);
sqlFunctions.sqlSaveTiddler({
title: "HelloThere",
text: "This is a shadow tiddler"
},"myplugin");
// Check that the shadow tiddler exists and has the expected value
checkExists("HelloThere",false,"Check the shadow tiddler does not exist");
checkTiddler("HelloThere",{
title: "HelloThere",
text: "This is a shadow tiddler"
},"Retrieve the tiddler");
checkShadowSource("HelloThere","myplugin","Check that the shadow source is correct");
sqlFunctions.sqlLogTables();
checkAllShadowTitles(["HelloThere"],"Check that the shadow title list is correct");
// Save an ordinary tiddler over the top and check it can be retrieved
sqlFunctions.sqlSaveTiddler({
title: "HelloThere",
text: "This is a tiddler"
});
checkExists("HelloThere",true,"Check the tiddler exists");
checkTiddler("HelloThere",{
title: "HelloThere",
text: "This is a tiddler"
},"Retrieve the tiddler");
checkAllTitles(["HelloThere"],"Check that the title list is correct");
checkShadowSource("HelloThere","myplugin","Check that the shadow source is correct");
checkAllShadowTitles(["HelloThere"],"Check that the shadow title list is correct");
// Delete the ordinary tiddler and check that the shadow tiddler is still available
sqlFunctions.sqlDeleteTiddler("HelloThere");
checkTiddler("HelloThere",{
title: "HelloThere",
text: "This is a shadow tiddler"
},"Try to retrieve the shadow tiddler exposed by the deleted tiddler");
checkShadowSource("HelloThere","myplugin","Check that the shadow source is correct");
checkAllShadowTitles(["HelloThere"],"Check that the shadow title list is correct");
});
});
// Run the tests
run();
};
//# sourceURL=$:/plugins/tiddlywiki/sqlite3store/test-sql-functions.js