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:
parent
1754be279f
commit
c52014c66f
@ -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();
|
||||
});
|
||||
|
@ -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>`
|
||||
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
167
plugins/tiddlywiki/sqlite3store/test-sql-functions.js
Normal file
167
plugins/tiddlywiki/sqlite3store/test-sql-functions.js
Normal 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
|
Loading…
Reference in New Issue
Block a user