mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
added foreign keys to relations to guarantee data consistency
This commit is contained in:
parent
bb2f47deb2
commit
72712bc24b
101
migrations/0057__add_foreign_keys.sql
Normal file
101
migrations/0057__add_foreign_keys.sql
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
INSERT INTO notes (note_id, note_title, note_text, date_created, date_modified)
|
||||||
|
VALUES ('root', 'root', 'root', strftime('%Y-%m-%dT%H:%M:%S.000Z', 'now'), strftime('%Y-%m-%dT%H:%M:%S.000Z', 'now'));
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "notes_mig" (
|
||||||
|
`note_id` TEXT NOT NULL,
|
||||||
|
`note_title` TEXT,
|
||||||
|
`note_text` TEXT,
|
||||||
|
`is_protected` INT NOT NULL DEFAULT 0,
|
||||||
|
`is_deleted` INT NOT NULL DEFAULT 0,
|
||||||
|
`date_created` TEXT NOT NULL,
|
||||||
|
`date_modified` TEXT NOT NULL,
|
||||||
|
PRIMARY KEY(`note_id`)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO notes_mig (note_id, note_title, note_text, is_protected, is_deleted, date_created, date_modified)
|
||||||
|
SELECT note_id, note_title, note_text, is_protected, is_deleted, date_created, date_modified FROM notes;
|
||||||
|
|
||||||
|
DROP TABLE notes;
|
||||||
|
ALTER TABLE notes_mig RENAME TO notes;
|
||||||
|
|
||||||
|
CREATE INDEX `IDX_notes_is_deleted` ON `notes` (
|
||||||
|
`is_deleted`
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "notes_tree_mig" (
|
||||||
|
`note_tree_id` TEXT NOT NULL,
|
||||||
|
`note_id` TEXT NOT NULL,
|
||||||
|
`parent_note_id` TEXT NOT NULL,
|
||||||
|
`note_position` INTEGER NOT NULL,
|
||||||
|
`prefix` TEXT,
|
||||||
|
`is_expanded` BOOLEAN,
|
||||||
|
`is_deleted` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`date_modified` TEXT NOT NULL,
|
||||||
|
FOREIGN KEY(note_id) REFERENCES notes(note_id),
|
||||||
|
FOREIGN KEY(parent_note_id) REFERENCES notes(note_id),
|
||||||
|
PRIMARY KEY(`note_tree_id`)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO notes_tree_mig (note_tree_id, note_id, parent_note_id, note_position, prefix, is_expanded, is_deleted, date_modified)
|
||||||
|
SELECT note_tree_id, note_id, note_pid, note_pos, prefix, is_expanded, is_deleted, date_modified FROM notes_tree;
|
||||||
|
|
||||||
|
DROP TABLE notes_tree;
|
||||||
|
ALTER TABLE notes_tree_mig RENAME TO notes_tree;
|
||||||
|
|
||||||
|
CREATE INDEX `IDX_notes_tree_note_tree_id` ON `notes_tree` (
|
||||||
|
`note_tree_id`
|
||||||
|
);
|
||||||
|
CREATE INDEX `IDX_notes_tree_note_id_parent_note_id` ON `notes_tree` (
|
||||||
|
`note_id`,
|
||||||
|
`parent_note_id`
|
||||||
|
);
|
||||||
|
CREATE INDEX `IDX_notes_tree_note_id` ON `notes_tree` (
|
||||||
|
`note_id`
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "notes_history_mig" (
|
||||||
|
`note_history_id` TEXT NOT NULL PRIMARY KEY,
|
||||||
|
`note_id` TEXT NOT NULL,
|
||||||
|
`note_title` TEXT,
|
||||||
|
`note_text` TEXT,
|
||||||
|
`is_protected` INT NOT NULL DEFAULT 0,
|
||||||
|
`date_modified_from` TEXT NOT NULL,
|
||||||
|
`date_modified_to` TEXT NOT NULL,
|
||||||
|
FOREIGN KEY(note_id) REFERENCES notes(note_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO notes_history_mig (note_history_id, note_id, note_title, note_text, is_protected, date_modified_from, date_modified_to)
|
||||||
|
SELECT note_history_id, note_id, note_title, note_text, is_protected, date_modified_from, date_modified_to FROM notes_history;
|
||||||
|
|
||||||
|
DROP TABLE notes_history;
|
||||||
|
ALTER TABLE notes_history_mig RENAME TO notes_history;
|
||||||
|
|
||||||
|
CREATE INDEX `IDX_notes_history_note_id` ON `notes_history` (
|
||||||
|
`note_id`
|
||||||
|
);
|
||||||
|
CREATE INDEX `IDX_notes_history_note_date_modified_from` ON `notes_history` (
|
||||||
|
`date_modified_from`
|
||||||
|
);
|
||||||
|
CREATE INDEX `IDX_notes_history_note_date_modified_to` ON `notes_history` (
|
||||||
|
`date_modified_to`
|
||||||
|
);
|
||||||
|
|
||||||
|
DROP TABLE recent_notes;
|
||||||
|
|
||||||
|
CREATE TABLE `recent_notes` (
|
||||||
|
`note_tree_id` TEXT NOT NULL PRIMARY KEY,
|
||||||
|
`note_path` TEXT NOT NULL,
|
||||||
|
`date_accessed` TEXT NOT NULL,
|
||||||
|
is_deleted INT,
|
||||||
|
FOREIGN KEY(note_tree_id) REFERENCES notes_tree(note_tree_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
DROP TABLE event_log;
|
||||||
|
|
||||||
|
CREATE TABLE `event_log` (
|
||||||
|
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
`note_id` TEXT,
|
||||||
|
`comment` TEXT,
|
||||||
|
`date_added` TEXT NOT NULL,
|
||||||
|
FOREIGN KEY(note_id) REFERENCES notes(note_id)
|
||||||
|
);
|
@ -88,7 +88,7 @@ const contextMenu = (function() {
|
|||||||
const node = $.ui.fancytree.getNode(ui.target);
|
const node = $.ui.fancytree.getNode(ui.target);
|
||||||
|
|
||||||
if (ui.cmd === "insertNoteHere") {
|
if (ui.cmd === "insertNoteHere") {
|
||||||
const parentNoteId = node.data.note_pid;
|
const parentNoteId = node.data.parent_note_id;
|
||||||
const isProtected = treeUtils.getParentProtectedStatus(node);
|
const isProtected = treeUtils.getParentProtectedStatus(node);
|
||||||
|
|
||||||
noteTree.createNote(node, parentNoteId, 'after', isProtected);
|
noteTree.createNote(node, parentNoteId, 'after', isProtected);
|
||||||
|
@ -132,7 +132,7 @@ const noteTree = (function() {
|
|||||||
|
|
||||||
delete note.note_title; // this should not be used. Use noteIdToTitle instead
|
delete note.note_title; // this should not be used. Use noteIdToTitle instead
|
||||||
|
|
||||||
setParentChildRelation(note.note_tree_id, note.note_pid, note.note_id);
|
setParentChildRelation(note.note_tree_id, note.parent_note_id, note.note_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return prepareNoteTreeInner('root');
|
return prepareNoteTreeInner('root');
|
||||||
@ -171,7 +171,7 @@ const noteTree = (function() {
|
|||||||
|
|
||||||
const node = {
|
const node = {
|
||||||
note_id: noteTree.note_id,
|
note_id: noteTree.note_id,
|
||||||
note_pid: noteTree.note_pid,
|
parent_note_id: noteTree.parent_note_id,
|
||||||
note_tree_id: noteTree.note_tree_id,
|
note_tree_id: noteTree.note_tree_id,
|
||||||
is_protected: noteTree.is_protected,
|
is_protected: noteTree.is_protected,
|
||||||
prefix: noteTree.prefix,
|
prefix: noteTree.prefix,
|
||||||
@ -207,7 +207,7 @@ const noteTree = (function() {
|
|||||||
//console.log(now(), "Run path: ", runPath);
|
//console.log(now(), "Run path: ", runPath);
|
||||||
|
|
||||||
for (const childNoteId of runPath) {
|
for (const childNoteId of runPath) {
|
||||||
const node = getNodesByNoteId(childNoteId).find(node => node.data.note_pid === parentNoteId);
|
const node = getNodesByNoteId(childNoteId).find(node => node.data.parent_note_id === parentNoteId);
|
||||||
|
|
||||||
if (childNoteId === noteId) {
|
if (childNoteId === noteId) {
|
||||||
await node.setActive();
|
await node.setActive();
|
||||||
@ -334,6 +334,10 @@ const noteTree = (function() {
|
|||||||
while (cur !== 'root') {
|
while (cur !== 'root') {
|
||||||
path.push(cur);
|
path.push(cur);
|
||||||
|
|
||||||
|
if (!childToParents[cur]) {
|
||||||
|
throwError("Can't find parents for " + cur);
|
||||||
|
}
|
||||||
|
|
||||||
cur = childToParents[cur][0];
|
cur = childToParents[cur][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,7 +618,7 @@ const noteTree = (function() {
|
|||||||
const newNode = {
|
const newNode = {
|
||||||
title: newNoteName,
|
title: newNoteName,
|
||||||
note_id: result.note_id,
|
note_id: result.note_id,
|
||||||
note_pid: parentNoteId,
|
parent_note_id: parentNoteId,
|
||||||
refKey: result.note_id,
|
refKey: result.note_id,
|
||||||
note_tree_id: result.note_tree_id,
|
note_tree_id: result.note_tree_id,
|
||||||
is_protected: isProtected,
|
is_protected: isProtected,
|
||||||
@ -646,7 +650,7 @@ const noteTree = (function() {
|
|||||||
console.log("pressed O");
|
console.log("pressed O");
|
||||||
|
|
||||||
const node = getCurrentNode();
|
const node = getCurrentNode();
|
||||||
const parentNoteId = node.data.note_pid;
|
const parentNoteId = node.data.parent_note_id;
|
||||||
const isProtected = treeUtils.getParentProtectedStatus(node);
|
const isProtected = treeUtils.getParentProtectedStatus(node);
|
||||||
|
|
||||||
createNote(node, parentNoteId, 'after', isProtected);
|
createNote(node, parentNoteId, 'after', isProtected);
|
||||||
|
@ -96,13 +96,13 @@ const treeChanges = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function changeNode(node, func) {
|
function changeNode(node, func) {
|
||||||
noteTree.removeParentChildRelation(node.data.note_pid, node.data.note_id);
|
noteTree.removeParentChildRelation(node.data.parent_note_id, node.data.note_id);
|
||||||
|
|
||||||
func(node);
|
func(node);
|
||||||
|
|
||||||
node.data.note_pid = node.getParent() === null ? 'root' : node.getParent().data.note_id;
|
node.data.parent_note_id = node.getParent() === null ? 'root' : node.getParent().data.note_id;
|
||||||
|
|
||||||
noteTree.setParentChildRelation(node.data.note_tree_id, node.data.note_pid, node.data.note_id);
|
noteTree.setParentChildRelation(node.data.note_tree_id, node.data.parent_note_id, node.data.note_id);
|
||||||
|
|
||||||
noteTree.setCurrentNotePathToHash(node);
|
noteTree.setCurrentNotePathToHash(node);
|
||||||
}
|
}
|
||||||
|
@ -35,11 +35,11 @@ async function exportNote(noteTreeId, dir) {
|
|||||||
const noteTree = await sql.getSingleResult("SELECT * FROM notes_tree WHERE note_tree_id = ?", [noteTreeId]);
|
const noteTree = await sql.getSingleResult("SELECT * FROM notes_tree WHERE note_tree_id = ?", [noteTreeId]);
|
||||||
const note = await sql.getSingleResult("SELECT * FROM notes WHERE note_id = ?", [noteTree.note_id]);
|
const note = await sql.getSingleResult("SELECT * FROM notes WHERE note_id = ?", [noteTree.note_id]);
|
||||||
|
|
||||||
const pos = (noteTree.note_pos + '').padStart(4, '0');
|
const pos = (noteTree.note_position + '').padStart(4, '0');
|
||||||
|
|
||||||
fs.writeFileSync(dir + '/' + pos + '-' + note.note_title + '.html', html.prettyPrint(note.note_text, {indent_size: 2}));
|
fs.writeFileSync(dir + '/' + pos + '-' + note.note_title + '.html', html.prettyPrint(note.note_text, {indent_size: 2}));
|
||||||
|
|
||||||
const children = await sql.getResults("SELECT * FROM notes_tree WHERE note_pid = ? AND is_deleted = 0", [note.note_id]);
|
const children = await sql.getResults("SELECT * FROM notes_tree WHERE parent_note_id = ? AND is_deleted = 0", [note.note_id]);
|
||||||
|
|
||||||
if (children.length > 0) {
|
if (children.length > 0) {
|
||||||
const childrenDir = dir + '/' + pos + '-' + note.note_title;
|
const childrenDir = dir + '/' + pos + '-' + note.note_title;
|
||||||
|
@ -51,7 +51,7 @@ async function importNotes(dir, parentNoteId) {
|
|||||||
noteTitle = match[2];
|
noteTitle = match[2];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let maxPos = await sql.getSingleValue("SELECT MAX(note_pos) FROM notes_tree WHERE note_pid = ? AND is_deleted = 0", [parentNoteId]);
|
let maxPos = await sql.getSingleValue("SELECT MAX(note_position) FROM notes_tree WHERE parent_note_id = ? AND is_deleted = 0", [parentNoteId]);
|
||||||
if (maxPos) {
|
if (maxPos) {
|
||||||
notePos = maxPos + 1;
|
notePos = maxPos + 1;
|
||||||
}
|
}
|
||||||
@ -72,8 +72,8 @@ async function importNotes(dir, parentNoteId) {
|
|||||||
await sql.insert('notes_tree', {
|
await sql.insert('notes_tree', {
|
||||||
note_tree_id: noteTreeId,
|
note_tree_id: noteTreeId,
|
||||||
note_id: noteId,
|
note_id: noteId,
|
||||||
note_pid: parentNoteId,
|
parent_note_id: parentNoteId,
|
||||||
note_pos: notePos,
|
note_position: notePos,
|
||||||
is_expanded: 0,
|
is_expanded: 0,
|
||||||
is_deleted: 0,
|
is_deleted: 0,
|
||||||
date_modified: now
|
date_modified: now
|
||||||
|
@ -12,13 +12,13 @@ router.put('/:noteTreeId/move-to/:parentNoteId', auth.checkApiAuth, async (req,
|
|||||||
const parentNoteId = req.params.parentNoteId;
|
const parentNoteId = req.params.parentNoteId;
|
||||||
const sourceId = req.headers.source_id;
|
const sourceId = req.headers.source_id;
|
||||||
|
|
||||||
const maxNotePos = await sql.getSingleValue('SELECT MAX(note_pos) FROM notes_tree WHERE note_pid = ? AND is_deleted = 0', [parentNoteId]);
|
const maxNotePos = await sql.getSingleValue('SELECT MAX(note_position) FROM notes_tree WHERE parent_note_id = ? AND is_deleted = 0', [parentNoteId]);
|
||||||
const newNotePos = maxNotePos === null ? 0 : maxNotePos + 1;
|
const newNotePos = maxNotePos === null ? 0 : maxNotePos + 1;
|
||||||
|
|
||||||
const now = utils.nowDate();
|
const now = utils.nowDate();
|
||||||
|
|
||||||
await sql.doInTransaction(async () => {
|
await sql.doInTransaction(async () => {
|
||||||
await sql.execute("UPDATE notes_tree SET note_pid = ?, note_pos = ?, date_modified = ? WHERE note_tree_id = ?",
|
await sql.execute("UPDATE notes_tree SET parent_note_id = ?, note_position = ?, date_modified = ? WHERE note_tree_id = ?",
|
||||||
[parentNoteId, newNotePos, now, noteTreeId]);
|
[parentNoteId, newNotePos, now, noteTreeId]);
|
||||||
|
|
||||||
await sync_table.addNoteTreeSync(noteTreeId, sourceId);
|
await sync_table.addNoteTreeSync(noteTreeId, sourceId);
|
||||||
@ -38,15 +38,15 @@ router.put('/:noteTreeId/move-before/:beforeNoteTreeId', async (req, res, next)
|
|||||||
await sql.doInTransaction(async () => {
|
await sql.doInTransaction(async () => {
|
||||||
// we don't change date_modified so other changes are prioritized in case of conflict
|
// we don't change date_modified so other changes are prioritized in case of conflict
|
||||||
// also we would have to sync all those modified note trees otherwise hash checks would fail
|
// also we would have to sync all those modified note trees otherwise hash checks would fail
|
||||||
await sql.execute("UPDATE notes_tree SET note_pos = note_pos + 1 WHERE note_pid = ? AND note_pos >= ? AND is_deleted = 0",
|
await sql.execute("UPDATE notes_tree SET note_position = note_position + 1 WHERE parent_note_id = ? AND note_position >= ? AND is_deleted = 0",
|
||||||
[beforeNote.note_pid, beforeNote.note_pos]);
|
[beforeNote.parent_note_id, beforeNote.note_position]);
|
||||||
|
|
||||||
await sync_table.addNoteReorderingSync(beforeNote.note_pid, sourceId);
|
await sync_table.addNoteReorderingSync(beforeNote.parent_note_id, sourceId);
|
||||||
|
|
||||||
const now = utils.nowDate();
|
const now = utils.nowDate();
|
||||||
|
|
||||||
await sql.execute("UPDATE notes_tree SET note_pid = ?, note_pos = ?, date_modified = ? WHERE note_tree_id = ?",
|
await sql.execute("UPDATE notes_tree SET parent_note_id = ?, note_position = ?, date_modified = ? WHERE note_tree_id = ?",
|
||||||
[beforeNote.note_pid, beforeNote.note_pos, now, noteTreeId]);
|
[beforeNote.parent_note_id, beforeNote.note_position, now, noteTreeId]);
|
||||||
|
|
||||||
await sync_table.addNoteTreeSync(noteTreeId, sourceId);
|
await sync_table.addNoteTreeSync(noteTreeId, sourceId);
|
||||||
});
|
});
|
||||||
@ -69,13 +69,13 @@ router.put('/:noteTreeId/move-after/:afterNoteTreeId', async (req, res, next) =>
|
|||||||
await sql.doInTransaction(async () => {
|
await sql.doInTransaction(async () => {
|
||||||
// we don't change date_modified so other changes are prioritized in case of conflict
|
// we don't change date_modified so other changes are prioritized in case of conflict
|
||||||
// also we would have to sync all those modified note trees otherwise hash checks would fail
|
// also we would have to sync all those modified note trees otherwise hash checks would fail
|
||||||
await sql.execute("UPDATE notes_tree SET note_pos = note_pos + 1 WHERE note_pid = ? AND note_pos > ? AND is_deleted = 0",
|
await sql.execute("UPDATE notes_tree SET note_position = note_position + 1 WHERE parent_note_id = ? AND note_position > ? AND is_deleted = 0",
|
||||||
[afterNote.note_pid, afterNote.note_pos]);
|
[afterNote.parent_note_id, afterNote.note_position]);
|
||||||
|
|
||||||
await sync_table.addNoteReorderingSync(afterNote.note_pid, sourceId);
|
await sync_table.addNoteReorderingSync(afterNote.parent_note_id, sourceId);
|
||||||
|
|
||||||
await sql.execute("UPDATE notes_tree SET note_pid = ?, note_pos = ?, date_modified = ? WHERE note_tree_id = ?",
|
await sql.execute("UPDATE notes_tree SET parent_note_id = ?, note_position = ?, date_modified = ? WHERE note_tree_id = ?",
|
||||||
[afterNote.note_pid, afterNote.note_pos + 1, utils.nowDate(), noteTreeId]);
|
[afterNote.parent_note_id, afterNote.note_position + 1, utils.nowDate(), noteTreeId]);
|
||||||
|
|
||||||
await sync_table.addNoteTreeSync(noteTreeId, sourceId);
|
await sync_table.addNoteTreeSync(noteTreeId, sourceId);
|
||||||
});
|
});
|
||||||
@ -92,7 +92,7 @@ router.put('/:childNoteId/clone-to/:parentNoteId', auth.checkApiAuth, async (req
|
|||||||
const childNoteId = req.params.childNoteId;
|
const childNoteId = req.params.childNoteId;
|
||||||
const sourceId = req.headers.source_id;
|
const sourceId = req.headers.source_id;
|
||||||
|
|
||||||
const existing = await sql.getSingleValue('SELECT * FROM notes_tree WHERE note_id = ? AND note_pid = ?', [childNoteId, parentNoteId]);
|
const existing = await sql.getSingleValue('SELECT * FROM notes_tree WHERE note_id = ? AND parent_note_id = ?', [childNoteId, parentNoteId]);
|
||||||
|
|
||||||
if (existing && !existing.is_deleted) {
|
if (existing && !existing.is_deleted) {
|
||||||
return res.send({
|
return res.send({
|
||||||
@ -108,15 +108,15 @@ router.put('/:childNoteId/clone-to/:parentNoteId', auth.checkApiAuth, async (req
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxNotePos = await sql.getSingleValue('SELECT MAX(note_pos) FROM notes_tree WHERE note_pid = ? AND is_deleted = 0', [parentNoteId]);
|
const maxNotePos = await sql.getSingleValue('SELECT MAX(note_position) FROM notes_tree WHERE parent_note_id = ? AND is_deleted = 0', [parentNoteId]);
|
||||||
const newNotePos = maxNotePos === null ? 0 : maxNotePos + 1;
|
const newNotePos = maxNotePos === null ? 0 : maxNotePos + 1;
|
||||||
|
|
||||||
await sql.doInTransaction(async () => {
|
await sql.doInTransaction(async () => {
|
||||||
const noteTree = {
|
const noteTree = {
|
||||||
note_tree_id: utils.newNoteTreeId(),
|
note_tree_id: utils.newNoteTreeId(),
|
||||||
note_id: childNoteId,
|
note_id: childNoteId,
|
||||||
note_pid: parentNoteId,
|
parent_note_id: parentNoteId,
|
||||||
note_pos: newNotePos,
|
note_position: newNotePos,
|
||||||
is_expanded: 0,
|
is_expanded: 0,
|
||||||
date_modified: utils.nowDate(),
|
date_modified: utils.nowDate(),
|
||||||
is_deleted: 0
|
is_deleted: 0
|
||||||
@ -143,14 +143,14 @@ router.put('/:noteId/clone-after/:afterNoteTreeId', async (req, res, next) => {
|
|||||||
return res.status(500).send("After note " + afterNoteTreeId + " doesn't exist.");
|
return res.status(500).send("After note " + afterNoteTreeId + " doesn't exist.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await checkCycle(afterNote.note_pid, noteId)) {
|
if (!await checkCycle(afterNote.parent_note_id, noteId)) {
|
||||||
return res.send({
|
return res.send({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Cloning note here would create cycle.'
|
message: 'Cloning note here would create cycle.'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const existing = await sql.getSingleValue('SELECT * FROM notes_tree WHERE note_id = ? AND note_pid = ?', [noteId, afterNote.note_pid]);
|
const existing = await sql.getSingleValue('SELECT * FROM notes_tree WHERE note_id = ? AND parent_note_id = ?', [noteId, afterNote.parent_note_id]);
|
||||||
|
|
||||||
if (existing && !existing.is_deleted) {
|
if (existing && !existing.is_deleted) {
|
||||||
return res.send({
|
return res.send({
|
||||||
@ -162,16 +162,16 @@ router.put('/:noteId/clone-after/:afterNoteTreeId', async (req, res, next) => {
|
|||||||
await sql.doInTransaction(async () => {
|
await sql.doInTransaction(async () => {
|
||||||
// we don't change date_modified so other changes are prioritized in case of conflict
|
// we don't change date_modified so other changes are prioritized in case of conflict
|
||||||
// also we would have to sync all those modified note trees otherwise hash checks would fail
|
// also we would have to sync all those modified note trees otherwise hash checks would fail
|
||||||
await sql.execute("UPDATE notes_tree SET note_pos = note_pos + 1 WHERE note_pid = ? AND note_pos > ? AND is_deleted = 0",
|
await sql.execute("UPDATE notes_tree SET note_position = note_position + 1 WHERE parent_note_id = ? AND note_position > ? AND is_deleted = 0",
|
||||||
[afterNote.note_pid, afterNote.note_pos]);
|
[afterNote.parent_note_id, afterNote.note_position]);
|
||||||
|
|
||||||
await sync_table.addNoteReorderingSync(afterNote.note_pid, sourceId);
|
await sync_table.addNoteReorderingSync(afterNote.parent_note_id, sourceId);
|
||||||
|
|
||||||
const noteTree = {
|
const noteTree = {
|
||||||
note_tree_id: utils.newNoteTreeId(),
|
note_tree_id: utils.newNoteTreeId(),
|
||||||
note_id: noteId,
|
note_id: noteId,
|
||||||
note_pid: afterNote.note_pid,
|
parent_note_id: afterNote.parent_note_id,
|
||||||
note_pos: afterNote.note_pos + 1,
|
note_position: afterNote.note_position + 1,
|
||||||
is_expanded: 0,
|
is_expanded: 0,
|
||||||
date_modified: utils.nowDate(),
|
date_modified: utils.nowDate(),
|
||||||
is_deleted: 0
|
is_deleted: 0
|
||||||
@ -196,7 +196,7 @@ async function checkCycle(parentNoteId, childNoteId) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parentNoteIds = await sql.getFlattenedResults("SELECT DISTINCT note_pid FROM notes_tree WHERE note_id = ?", [parentNoteId]);
|
const parentNoteIds = await sql.getFlattenedResults("SELECT DISTINCT parent_note_id FROM notes_tree WHERE note_id = ?", [parentNoteId]);
|
||||||
|
|
||||||
for (const pid of parentNoteIds) {
|
for (const pid of parentNoteIds) {
|
||||||
if (!await checkCycle(pid, childNoteId)) {
|
if (!await checkCycle(pid, childNoteId)) {
|
||||||
|
@ -73,8 +73,8 @@ router.get('/notes_reordering/:noteTreeParentId', auth.checkApiAuth, async (req,
|
|||||||
const noteTreeParentId = req.params.noteTreeParentId;
|
const noteTreeParentId = req.params.noteTreeParentId;
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
note_pid: noteTreeParentId,
|
parent_note_id: noteTreeParentId,
|
||||||
ordering: await sql.getMap("SELECT note_tree_id, note_pos FROM notes_tree WHERE note_pid = ?", [noteTreeParentId])
|
ordering: await sql.getMap("SELECT note_tree_id, note_position FROM notes_tree WHERE parent_note_id = ?", [noteTreeParentId])
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ router.get('/', auth.checkApiAuth, async (req, res, next) => {
|
|||||||
+ "FROM notes_tree "
|
+ "FROM notes_tree "
|
||||||
+ "JOIN notes ON notes.note_id = notes_tree.note_id "
|
+ "JOIN notes ON notes.note_id = notes_tree.note_id "
|
||||||
+ "WHERE notes.is_deleted = 0 AND notes_tree.is_deleted = 0 "
|
+ "WHERE notes.is_deleted = 0 AND notes_tree.is_deleted = 0 "
|
||||||
+ "ORDER BY note_pos");
|
+ "ORDER BY note_position");
|
||||||
|
|
||||||
const dataKey = protected_session.getDataKey(req);
|
const dataKey = protected_session.getDataKey(req);
|
||||||
|
|
||||||
|
@ -70,8 +70,8 @@ CREATE TABLE `recent_notes` (
|
|||||||
CREATE TABLE IF NOT EXISTS "notes_tree" (
|
CREATE TABLE IF NOT EXISTS "notes_tree" (
|
||||||
`note_tree_id` TEXT NOT NULL,
|
`note_tree_id` TEXT NOT NULL,
|
||||||
`note_id` TEXT NOT NULL,
|
`note_id` TEXT NOT NULL,
|
||||||
`note_pid` TEXT NOT NULL,
|
`parent_note_id` TEXT NOT NULL,
|
||||||
`note_pos` INTEGER NOT NULL,
|
`note_position` INTEGER NOT NULL,
|
||||||
`prefix` TEXT,
|
`prefix` TEXT,
|
||||||
`is_expanded` BOOLEAN,
|
`is_expanded` BOOLEAN,
|
||||||
`is_deleted` INTEGER NOT NULL DEFAULT 0,
|
`is_deleted` INTEGER NOT NULL DEFAULT 0,
|
||||||
@ -81,7 +81,7 @@ CREATE TABLE IF NOT EXISTS "notes_tree" (
|
|||||||
CREATE INDEX `IDX_notes_tree_note_id` ON `notes_tree` (
|
CREATE INDEX `IDX_notes_tree_note_id` ON `notes_tree` (
|
||||||
`note_id`
|
`note_id`
|
||||||
);
|
);
|
||||||
CREATE INDEX `IDX_notes_tree_note_id_note_pid` ON `notes_tree` (
|
CREATE INDEX `IDX_notes_tree_note_id_parent_note_id` ON `notes_tree` (
|
||||||
`note_id`,
|
`note_id`,
|
||||||
`note_pid`
|
`parent_note_id`
|
||||||
);
|
);
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
const build = require('./build');
|
const build = require('./build');
|
||||||
const packageJson = require('../package');
|
const packageJson = require('../package');
|
||||||
|
|
||||||
const APP_DB_VERSION = 56;
|
const APP_DB_VERSION = 57;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
app_version: packageJson.version,
|
app_version: packageJson.version,
|
||||||
|
@ -16,7 +16,7 @@ async function runCheck(query, errorText, errorList) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function runSyncRowChecks(table, key, errorList) {
|
async function runSyncRowChecks(table, key, errorList) {
|
||||||
await runCheck(`SELECT ${key} FROM ${table} LEFT JOIN sync ON sync.entity_name = '${table}' AND entity_id = ${key} WHERE sync.id IS NULL`,
|
await runCheck(`SELECT ${key} FROM ${table} LEFT JOIN sync ON sync.entity_name = '${table}' AND entity_id = ${key} WHERE entity_id != 'root' AND sync.id IS NULL`,
|
||||||
`Missing sync records for ${key} in table ${table}`, errorList);
|
`Missing sync records for ${key} in table ${table}`, errorList);
|
||||||
|
|
||||||
await runCheck(`SELECT entity_id FROM sync LEFT JOIN ${table} ON entity_id = ${key} WHERE sync.entity_name = '${table}' AND ${key} IS NULL`,
|
await runCheck(`SELECT entity_id FROM sync LEFT JOIN ${table} ON entity_id = ${key} WHERE sync.entity_name = '${table}' AND ${key} IS NULL`,
|
||||||
@ -26,7 +26,7 @@ async function runSyncRowChecks(table, key, errorList) {
|
|||||||
async function runChecks() {
|
async function runChecks() {
|
||||||
const errorList = [];
|
const errorList = [];
|
||||||
|
|
||||||
await runCheck("SELECT note_id FROM notes LEFT JOIN notes_tree USING(note_id) WHERE notes_tree.note_tree_id IS NULL",
|
await runCheck("SELECT note_id FROM notes LEFT JOIN notes_tree USING(note_id) WHERE note_id != 'root' AND notes_tree.note_tree_id IS NULL",
|
||||||
"Missing notes_tree records for following note IDs", errorList);
|
"Missing notes_tree records for following note IDs", errorList);
|
||||||
|
|
||||||
await runCheck("SELECT note_tree_id || ' > ' || notes_tree.note_id FROM notes_tree LEFT JOIN notes USING(note_id) WHERE notes.note_id IS NULL",
|
await runCheck("SELECT note_tree_id || ' > ' || notes_tree.note_id FROM notes_tree LEFT JOIN notes USING(note_id) WHERE notes.note_id IS NULL",
|
||||||
@ -35,7 +35,7 @@ async function runChecks() {
|
|||||||
await runCheck("SELECT note_tree_id FROM notes_tree JOIN notes USING(note_id) WHERE notes.is_deleted = 1 AND notes_tree.is_deleted = 0",
|
await runCheck("SELECT note_tree_id FROM notes_tree JOIN notes USING(note_id) WHERE notes.is_deleted = 1 AND notes_tree.is_deleted = 0",
|
||||||
"Note tree is not deleted even though main note is deleted for following note tree IDs", errorList);
|
"Note tree is not deleted even though main note is deleted for following note tree IDs", errorList);
|
||||||
|
|
||||||
await runCheck("SELECT child.note_pid || ' > ' || child.note_id FROM notes_tree AS child LEFT JOIN notes_tree AS parent ON parent.note_id = child.note_pid WHERE parent.note_id IS NULL AND child.note_pid != 'root'",
|
await runCheck("SELECT child.parent_note_id || ' > ' || child.note_id FROM notes_tree AS child LEFT JOIN notes_tree AS parent ON parent.note_id = child.parent_note_id WHERE parent.note_id IS NULL AND child.parent_note_id != 'root'",
|
||||||
"Not existing parent in the following parent > child relations", errorList);
|
"Not existing parent in the following parent > child relations", errorList);
|
||||||
|
|
||||||
await runCheck("SELECT note_history_id || ' > ' || notes_history.note_id FROM notes_history LEFT JOIN notes USING(note_id) WHERE notes.note_id IS NULL",
|
await runCheck("SELECT note_history_id || ' > ' || notes_history.note_id FROM notes_history LEFT JOIN notes USING(note_id) WHERE notes.note_id IS NULL",
|
||||||
|
@ -29,8 +29,8 @@ async function getHashes() {
|
|||||||
notes_tree: getHash(await sql.getResults(`SELECT
|
notes_tree: getHash(await sql.getResults(`SELECT
|
||||||
note_tree_id,
|
note_tree_id,
|
||||||
note_id,
|
note_id,
|
||||||
note_pid,
|
parent_note_id,
|
||||||
note_pos,
|
note_position,
|
||||||
date_modified,
|
date_modified,
|
||||||
is_deleted,
|
is_deleted,
|
||||||
prefix
|
prefix
|
||||||
|
@ -13,18 +13,18 @@ async function createNewNote(parentNoteId, note, sourceId) {
|
|||||||
|
|
||||||
await sql.doInTransaction(async () => {
|
await sql.doInTransaction(async () => {
|
||||||
if (note.target === 'into') {
|
if (note.target === 'into') {
|
||||||
const maxNotePos = await sql.getSingleValue('SELECT MAX(note_pos) FROM notes_tree WHERE note_pid = ? AND is_deleted = 0', [parentNoteId]);
|
const maxNotePos = await sql.getSingleValue('SELECT MAX(note_position) FROM notes_tree WHERE parent_note_id = ? AND is_deleted = 0', [parentNoteId]);
|
||||||
|
|
||||||
newNotePos = maxNotePos === null ? 0 : maxNotePos + 1;
|
newNotePos = maxNotePos === null ? 0 : maxNotePos + 1;
|
||||||
}
|
}
|
||||||
else if (note.target === 'after') {
|
else if (note.target === 'after') {
|
||||||
const afterNote = await sql.getSingleResult('SELECT note_pos FROM notes_tree WHERE note_tree_id = ?', [note.target_note_tree_id]);
|
const afterNote = await sql.getSingleResult('SELECT note_position FROM notes_tree WHERE note_tree_id = ?', [note.target_note_tree_id]);
|
||||||
|
|
||||||
newNotePos = afterNote.note_pos + 1;
|
newNotePos = afterNote.note_position + 1;
|
||||||
|
|
||||||
// not updating date_modified to avoig having to sync whole rows
|
// not updating date_modified to avoig having to sync whole rows
|
||||||
await sql.execute('UPDATE notes_tree SET note_pos = note_pos + 1 WHERE note_pid = ? AND note_pos > ? AND is_deleted = 0',
|
await sql.execute('UPDATE notes_tree SET note_position = note_position + 1 WHERE parent_note_id = ? AND note_position > ? AND is_deleted = 0',
|
||||||
[parentNoteId, afterNote.note_pos]);
|
[parentNoteId, afterNote.note_position]);
|
||||||
|
|
||||||
await sync_table.addNoteReorderingSync(parentNoteId, sourceId);
|
await sync_table.addNoteReorderingSync(parentNoteId, sourceId);
|
||||||
}
|
}
|
||||||
@ -48,8 +48,8 @@ async function createNewNote(parentNoteId, note, sourceId) {
|
|||||||
await sql.insert("notes_tree", {
|
await sql.insert("notes_tree", {
|
||||||
note_tree_id: noteTreeId,
|
note_tree_id: noteTreeId,
|
||||||
note_id: noteId,
|
note_id: noteId,
|
||||||
note_pid: parentNoteId,
|
parent_note_id: parentNoteId,
|
||||||
note_pos: newNotePos,
|
note_position: newNotePos,
|
||||||
is_expanded: 0,
|
is_expanded: 0,
|
||||||
date_modified: now,
|
date_modified: now,
|
||||||
is_deleted: 0
|
is_deleted: 0
|
||||||
@ -74,7 +74,7 @@ async function protectNoteRecursively(noteId, dataKey, protect, sourceId) {
|
|||||||
|
|
||||||
await protectNote(note, dataKey, protect, sourceId);
|
await protectNote(note, dataKey, protect, sourceId);
|
||||||
|
|
||||||
const children = await sql.getFlattenedResults("SELECT note_id FROM notes_tree WHERE note_pid = ?", [noteId]);
|
const children = await sql.getFlattenedResults("SELECT note_id FROM notes_tree WHERE parent_note_id = ?", [noteId]);
|
||||||
|
|
||||||
for (const childNoteId of children) {
|
for (const childNoteId of children) {
|
||||||
await protectNoteRecursively(childNoteId, dataKey, protect, sourceId);
|
await protectNoteRecursively(childNoteId, dataKey, protect, sourceId);
|
||||||
@ -205,7 +205,7 @@ async function deleteNote(noteTreeId, sourceId) {
|
|||||||
await sql.execute("UPDATE notes SET is_deleted = 1, date_modified = ? WHERE note_id = ?", [now, noteId]);
|
await sql.execute("UPDATE notes SET is_deleted = 1, date_modified = ? WHERE note_id = ?", [now, noteId]);
|
||||||
await sync_table.addNoteSync(noteId, sourceId);
|
await sync_table.addNoteSync(noteId, sourceId);
|
||||||
|
|
||||||
const children = await sql.getResults("SELECT note_tree_id FROM notes_tree WHERE note_pid = ? AND is_deleted = 0", [noteId]);
|
const children = await sql.getResults("SELECT note_tree_id FROM notes_tree WHERE parent_note_id = ? AND is_deleted = 0", [noteId]);
|
||||||
|
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
await deleteNote(child.note_tree_id, sourceId);
|
await deleteNote(child.note_tree_id, sourceId);
|
||||||
|
@ -37,8 +37,8 @@ const dbReady = new Promise((resolve, reject) => {
|
|||||||
await insert('notes_tree', {
|
await insert('notes_tree', {
|
||||||
note_tree_id: utils.newNoteTreeId(),
|
note_tree_id: utils.newNoteTreeId(),
|
||||||
note_id: noteId,
|
note_id: noteId,
|
||||||
note_pid: 'root',
|
parent_note_id: 'root',
|
||||||
note_pos: 1,
|
note_position: 1,
|
||||||
is_deleted: 0,
|
is_deleted: 0,
|
||||||
date_modified: now
|
date_modified: now
|
||||||
});
|
});
|
||||||
|
@ -209,8 +209,8 @@ async function pushEntity(sync, syncContext) {
|
|||||||
}
|
}
|
||||||
else if (sync.entity_name === 'notes_reordering') {
|
else if (sync.entity_name === 'notes_reordering') {
|
||||||
entity = {
|
entity = {
|
||||||
note_pid: sync.entity_id,
|
parent_note_id: sync.entity_id,
|
||||||
ordering: await sql.getMap('SELECT note_tree_id, note_pos FROM notes_tree WHERE note_pid = ?', [sync.entity_id])
|
ordering: await sql.getMap('SELECT note_tree_id, note_position FROM notes_tree WHERE parent_note_id = ?', [sync.entity_id])
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (sync.entity_name === 'options') {
|
else if (sync.entity_name === 'options') {
|
||||||
|
@ -53,10 +53,10 @@ async function updateNoteHistory(entity, sourceId) {
|
|||||||
async function updateNoteReordering(entity, sourceId) {
|
async function updateNoteReordering(entity, sourceId) {
|
||||||
await sql.doInTransaction(async () => {
|
await sql.doInTransaction(async () => {
|
||||||
Object.keys(entity.ordering).forEach(async key => {
|
Object.keys(entity.ordering).forEach(async key => {
|
||||||
await sql.execute("UPDATE notes_tree SET note_pos = ? WHERE note_tree_id = ?", [entity.ordering[key], key]);
|
await sql.execute("UPDATE notes_tree SET note_position = ? WHERE note_tree_id = ?", [entity.ordering[key], key]);
|
||||||
});
|
});
|
||||||
|
|
||||||
await sync_table.addNoteReorderingSync(entity.note_pid, sourceId);
|
await sync_table.addNoteReorderingSync(entity.parent_note_id, sourceId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user