WIP blobs

This commit is contained in:
zadam 2023-03-16 11:02:07 +01:00
parent 5a8e216dec
commit e16bedfab4
30 changed files with 65 additions and 102 deletions

View File

@ -1,10 +1,8 @@
UPDATE etapi_tokens SET tokenHash = 'API token hash value'; UPDATE etapi_tokens SET tokenHash = 'API token hash value';
UPDATE notes SET title = 'title' WHERE noteId != 'root' AND noteId NOT LIKE '\_%' ESCAPE '\'; UPDATE notes SET title = 'title' WHERE noteId != 'root' AND noteId NOT LIKE '\_%' ESCAPE '\';
UPDATE note_contents SET content = 'text' WHERE content IS NOT NULL; UPDATE blobs SET content = 'text' WHERE content IS NOT NULL;
UPDATE note_revisions SET title = 'title'; UPDATE note_revisions SET title = 'title';
UPDATE note_revision_contents SET content = 'text' WHERE content IS NOT NULL;
UPDATE note_ancillary_contents SET content = 'text' WHERE content IS NOT NULL;
UPDATE attributes SET name = 'name', value = 'value' UPDATE attributes SET name = 'name', value = 'value'
WHERE type = 'label' WHERE type = 'label'

View File

@ -5,7 +5,6 @@ CREATE TABLE IF NOT EXISTS "note_ancillaries"
name TEXT not null, name TEXT not null,
mime TEXT not null, mime TEXT not null,
isProtected INT not null DEFAULT 0, isProtected INT not null DEFAULT 0,
contentCheckSum TEXT not null,
blobId TEXT not null, blobId TEXT not null,
utcDateModified TEXT not null, utcDateModified TEXT not null,
isDeleted INT not null, isDeleted INT not null,

View File

@ -119,13 +119,10 @@ CREATE TABLE IF NOT EXISTS "note_ancillaries"
name TEXT not null, name TEXT not null,
mime TEXT not null, mime TEXT not null,
isProtected INT not null DEFAULT 0, isProtected INT not null DEFAULT 0,
contentCheckSum TEXT not null,
utcDateModified TEXT not null, utcDateModified TEXT not null,
isDeleted INT not null, isDeleted INT not null,
`deleteId` TEXT DEFAULT NULL); `deleteId` TEXT DEFAULT NULL);
CREATE TABLE IF NOT EXISTS "note_ancillary_contents" (`noteAncillaryId` TEXT NOT NULL PRIMARY KEY,
`content` TEXT DEFAULT NULL,
`utcDateModified` TEXT NOT NULL);
CREATE INDEX IDX_note_ancillaries_name CREATE INDEX IDX_note_ancillaries_name
on note_ancillaries (name); on note_ancillaries (name);
CREATE UNIQUE INDEX IDX_note_ancillaries_noteId_name CREATE UNIQUE INDEX IDX_note_ancillaries_noteId_name

View File

@ -74,7 +74,7 @@ function dumpDocument(documentPath, targetPath, options) {
return; return;
} }
let {content} = sql.getRow("SELECT content FROM note_contents WHERE noteId = ?", [noteId]); let {content} = sql.getRow("SELECT content FROM blobs WHERE blobId = ?", [note.blobId]);
if (content !== null && note.isProtected && dataKey) { if (content !== null && note.isProtected && dataKey) {
content = decryptService.decrypt(dataKey, content); content = decryptService.decrypt(dataKey, content);

View File

@ -125,7 +125,7 @@ class Becca {
getNoteAncillary(noteAncillaryId) { getNoteAncillary(noteAncillaryId) {
const row = sql.getRow("SELECT * FROM note_ancillaries WHERE noteAncillaryId = ?", [noteAncillaryId]); const row = sql.getRow("SELECT * FROM note_ancillaries WHERE noteAncillaryId = ?", [noteAncillaryId]);
const BNoteAncillary = require("./entities/bnote_ancillary"); // avoiding circular dependency problems const BNoteAncillary = require("./entities/bnote_attachment.js"); // avoiding circular dependency problems
return row ? new BNoteAncillary(row) : null; return row ? new BNoteAncillary(row) : null;
} }

View File

@ -8,7 +8,7 @@ const dateUtils = require('../../services/date_utils');
const entityChangesService = require('../../services/entity_changes'); const entityChangesService = require('../../services/entity_changes');
const AbstractBeccaEntity = require("./abstract_becca_entity"); const AbstractBeccaEntity = require("./abstract_becca_entity");
const BNoteRevision = require("./bnote_revision"); const BNoteRevision = require("./bnote_revision");
const BNoteAncillary = require("./bnote_ancillary"); const BNoteAncillary = require("./bnote_attachment.js");
const TaskContext = require("../../services/task_context"); const TaskContext = require("../../services/task_context");
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const utc = require('dayjs/plugin/utc'); const utc = require('dayjs/plugin/utc');
@ -1507,13 +1507,6 @@ class BNote extends AbstractBeccaEntity {
saveNoteAncillary(name, mime, content) { saveNoteAncillary(name, mime, content) {
let noteAncillary = this.getNoteAncillaryByName(name); let noteAncillary = this.getNoteAncillaryByName(name);
if (noteAncillary
&& noteAncillary.mime === mime
&& noteAncillary.contentCheckSum === noteAncillary.calculateCheckSum(content)) {
return noteAncillary; // no change
}
noteAncillary = new BNoteAncillary({ noteAncillary = new BNoteAncillary({
noteId: this.noteId, noteId: this.noteId,
name, name,

View File

@ -41,8 +41,6 @@ class BNoteAncillary extends AbstractBeccaEntity {
/** @type {boolean} */ /** @type {boolean} */
this.isProtected = !!row.isProtected; this.isProtected = !!row.isProtected;
/** @type {string} */ /** @type {string} */
this.contentCheckSum = row.contentCheckSum;
/** @type {string} */
this.utcDateModified = row.utcDateModified; this.utcDateModified = row.utcDateModified;
} }
@ -91,7 +89,6 @@ class BNoteAncillary extends AbstractBeccaEntity {
setContent(content) { setContent(content) {
sql.transactional(() => { sql.transactional(() => {
this.contentCheckSum = this.calculateCheckSum(content);
this.save(); // also explicitly save note_ancillary to update contentCheckSum this.save(); // also explicitly save note_ancillary to update contentCheckSum
const pojo = { const pojo = {
@ -113,7 +110,7 @@ class BNoteAncillary extends AbstractBeccaEntity {
entityChangesService.addEntityChange({ entityChangesService.addEntityChange({
entityName: 'note_ancillary_contents', entityName: 'note_ancillary_contents',
entityId: this.noteAncillaryId, entityId: this.noteAncillaryId,
hash: this.contentCheckSum, hash: this.contentCheckSum, // FIXME
isErased: false, isErased: false,
utcDateChanged: pojo.utcDateModified, utcDateChanged: pojo.utcDateModified,
isSynced: true isSynced: true
@ -144,7 +141,7 @@ class BNoteAncillary extends AbstractBeccaEntity {
name: this.name, name: this.name,
mime: this.mime, mime: this.mime,
isProtected: !!this.isProtected, isProtected: !!this.isProtected,
contentCheckSum: this.contentCheckSum, contentCheckSum: this.contentCheckSum, // FIXME
isDeleted: false, isDeleted: false,
utcDateModified: this.utcDateModified utcDateModified: this.utcDateModified
}; };

View File

@ -1,6 +1,6 @@
const BNote = require('./entities/bnote'); const BNote = require('./entities/bnote');
const BNoteRevision = require('./entities/bnote_revision'); const BNoteRevision = require('./entities/bnote_revision');
const BNoteAncillary = require("./entities/bnote_ancillary"); const BNoteAncillary = require("./entities/bnote_attachment.js");
const BBranch = require('./entities/bbranch'); const BBranch = require('./entities/bbranch');
const BAttribute = require('./entities/battribute'); const BAttribute = require('./entities/battribute');
const BRecentNote = require('./entities/brecent_note'); const BRecentNote = require('./entities/brecent_note');
@ -11,11 +11,8 @@ const ENTITY_NAME_TO_ENTITY = {
"attributes": BAttribute, "attributes": BAttribute,
"branches": BBranch, "branches": BBranch,
"notes": BNote, "notes": BNote,
"note_contents": BNote,
"note_revisions": BNoteRevision, "note_revisions": BNoteRevision,
"note_revision_contents": BNoteRevision,
"note_ancillaries": BNoteAncillary, "note_ancillaries": BNoteAncillary,
"note_ancillary_contents": BNoteAncillary,
"recent_notes": BRecentNote, "recent_notes": BRecentNote,
"etapi_tokens": BEtapiToken, "etapi_tokens": BEtapiToken,
"options": BOption "options": BOption

View File

@ -25,8 +25,6 @@ async function processEntityChanges(entityChanges) {
loadResults.addNoteContent(ec.noteIds, ec.componentId); loadResults.addNoteContent(ec.noteIds, ec.componentId);
} else if (ec.entityName === 'note_revisions') { } else if (ec.entityName === 'note_revisions') {
loadResults.addNoteRevision(ec.entityId, ec.noteId, ec.componentId); loadResults.addNoteRevision(ec.entityId, ec.noteId, ec.componentId);
} else if (ec.entityName === 'note_revision_contents') {
// this should change only when toggling isProtected, ignore
} else if (ec.entityName === 'options') { } else if (ec.entityName === 'options') {
if (ec.entity.name === 'openTabs') { if (ec.entity.name === 'openTabs') {
continue; // only noise continue; // only noise
@ -36,7 +34,7 @@ async function processEntityChanges(entityChanges) {
loadResults.addOption(ec.entity.name); loadResults.addOption(ec.entity.name);
} }
else if (['etapi_tokens', 'note_ancillaries', 'note_ancillary_contents'].includes(ec.entityName)) { else if (['etapi_tokens', 'note_ancillaries'].includes(ec.entityName)) {
// NOOP // NOOP
} }
else { else {

View File

@ -27,7 +27,7 @@ import NoteMapTypeWidget from "./type_widgets/note_map.js";
import WebViewTypeWidget from "./type_widgets/web_view.js"; import WebViewTypeWidget from "./type_widgets/web_view.js";
import DocTypeWidget from "./type_widgets/doc.js"; import DocTypeWidget from "./type_widgets/doc.js";
import ContentWidgetTypeWidget from "./type_widgets/content_widget.js"; import ContentWidgetTypeWidget from "./type_widgets/content_widget.js";
import AncillariesTypeWidget from "./type_widgets/ancillaries.js"; import AncillariesTypeWidget from "./type_widgets/attachments.js";
const TPL = ` const TPL = `
<div class="note-detail"> <div class="note-detail">

View File

@ -12,9 +12,9 @@ const becca = require("../../becca/becca");
function getNoteRevisions(req) { function getNoteRevisions(req) {
return becca.getNoteRevisionsFromQuery(` return becca.getNoteRevisionsFromQuery(`
SELECT note_revisions.*, SELECT note_revisions.*,
LENGTH(note_revision_contents.content) AS contentLength LENGTH(blobs.content) AS contentLength
FROM note_revisions FROM note_revisions
JOIN note_revision_contents ON note_revisions.noteRevisionId = note_revision_contents.noteRevisionId JOIN blobs ON note_revisions.blobId = blobs.blobId
WHERE noteId = ? WHERE noteId = ?
ORDER BY utcDateCreated DESC`, [req.params.noteId]); ORDER BY utcDateCreated DESC`, [req.params.noteId]);
} }

View File

@ -4,18 +4,19 @@ const NotFoundError = require("../../errors/not_found_error");
function getNoteSize(req) { function getNoteSize(req) {
const {noteId} = req.params; const {noteId} = req.params;
const note = becca.getNote(noteId);
const noteSize = sql.getValue(` const noteSize = sql.getValue(`
SELECT SELECT
COALESCE((SELECT LENGTH(content) FROM note_contents WHERE noteId = ?), 0) COALESCE((SELECT LENGTH(content) FROM blobs WHERE blobId = ?), 0)
+ +
COALESCE( COALESCE(
(SELECT SUM(LENGTH(content)) (SELECT SUM(LENGTH(content))
FROM note_revisions FROM note_revisions
JOIN note_revision_contents USING (noteRevisionId) JOIN blobs USING (blobId)
WHERE note_revisions.noteId = ?), WHERE note_revisions.noteId = ?),
0 0
)`, [noteId, noteId]); )`, [note.blobId, noteId]);
return { return {
noteSize noteSize
@ -38,14 +39,15 @@ function getSubtreeSize(req) {
SELECT SELECT
COALESCE(( COALESCE((
SELECT SUM(LENGTH(content)) SELECT SUM(LENGTH(content))
FROM note_contents FROM notes
JOIN param_list ON param_list.paramId = note_contents.noteId JOIN blobs USING (blobId)
JOIN param_list ON param_list.paramId = notes.noteId
), 0) ), 0)
+ +
COALESCE( COALESCE(
(SELECT SUM(LENGTH(content)) (SELECT SUM(LENGTH(content))
FROM note_revisions FROM note_revisions
JOIN note_revision_contents USING (noteRevisionId) JOIN blobs USING (blobId)
JOIN param_list ON param_list.paramId = note_revisions.noteId), JOIN param_list ON param_list.paramId = note_revisions.noteId),
0 0
)`); )`);

View File

@ -12,6 +12,7 @@ const syncOptions = require('../../services/sync_options');
const dateUtils = require('../../services/date_utils'); const dateUtils = require('../../services/date_utils');
const utils = require('../../services/utils'); const utils = require('../../services/utils');
const ws = require('../../services/ws'); const ws = require('../../services/ws');
const becca = require("../../becca/becca.js");
async function testSync() { async function testSync() {
try { try {
@ -85,14 +86,15 @@ function forceFullSync() {
function forceNoteSync(req) { function forceNoteSync(req) {
const noteId = req.params.noteId; const noteId = req.params.noteId;
const note = becca.getNote(noteId);
const now = dateUtils.utcNowDateTime(); const now = dateUtils.utcNowDateTime();
sql.execute(`UPDATE notes SET utcDateModified = ? WHERE noteId = ?`, [now, noteId]); sql.execute(`UPDATE notes SET utcDateModified = ? WHERE noteId = ?`, [now, noteId]);
entityChangesService.moveEntityChangeToTop('notes', noteId); entityChangesService.moveEntityChangeToTop('notes', noteId);
sql.execute(`UPDATE note_contents SET utcDateModified = ? WHERE noteId = ?`, [now, noteId]); sql.execute(`UPDATE blobs SET utcDateModified = ? WHERE blobId = ?`, [now, note.blobId]);
entityChangesService.moveEntityChangeToTop('note_contents', noteId); entityChangesService.moveEntityChangeToTop('blobs', note.blobId);
for (const branchId of sql.getColumn("SELECT branchId FROM branches WHERE noteId = ?", [noteId])) { for (const branchId of sql.getColumn("SELECT branchId FROM branches WHERE noteId = ?", [noteId])) {
sql.execute(`UPDATE branches SET utcDateModified = ? WHERE branchId = ?`, [now, branchId]); sql.execute(`UPDATE branches SET utcDateModified = ? WHERE branchId = ?`, [now, branchId]);
@ -109,17 +111,11 @@ function forceNoteSync(req) {
for (const noteRevisionId of sql.getColumn("SELECT noteRevisionId FROM note_revisions WHERE noteId = ?", [noteId])) { for (const noteRevisionId of sql.getColumn("SELECT noteRevisionId FROM note_revisions WHERE noteId = ?", [noteId])) {
sql.execute(`UPDATE note_revisions SET utcDateModified = ? WHERE noteRevisionId = ?`, [now, noteRevisionId]); sql.execute(`UPDATE note_revisions SET utcDateModified = ? WHERE noteRevisionId = ?`, [now, noteRevisionId]);
entityChangesService.moveEntityChangeToTop('note_revisions', noteRevisionId); entityChangesService.moveEntityChangeToTop('note_revisions', noteRevisionId);
sql.execute(`UPDATE note_revision_contents SET utcDateModified = ? WHERE noteRevisionId = ?`, [now, noteRevisionId]);
entityChangesService.moveEntityChangeToTop('note_revision_contents', noteRevisionId);
} }
for (const noteAncillaryId of sql.getColumn("SELECT noteAncillaryId FROM note_ancillaries WHERE noteId = ?", [noteId])) { for (const noteAncillaryId of sql.getColumn("SELECT noteAncillaryId FROM note_ancillaries WHERE noteId = ?", [noteId])) {
sql.execute(`UPDATE note_ancillaries SET utcDateModified = ? WHERE noteAncillaryId = ?`, [now, noteAncillaryId]); sql.execute(`UPDATE note_ancillaries SET utcDateModified = ? WHERE noteAncillaryId = ?`, [now, noteAncillaryId]);
entityChangesService.moveEntityChangeToTop('note_ancillaries', noteAncillaryId); entityChangesService.moveEntityChangeToTop('note_ancillaries', noteAncillaryId);
sql.execute(`UPDATE note_ancillary_contents SET utcDateModified = ? WHERE noteAncillaryId = ?`, [now, noteAncillaryId]);
entityChangesService.moveEntityChangeToTop('note_ancillary_contents', noteAncillaryId);
} }
log.info(`Forcing note sync for ${noteId}`); log.info(`Forcing note sync for ${noteId}`);

View File

@ -14,9 +14,8 @@ function getFullAnonymizationScript() {
const anonymizeScript = ` const anonymizeScript = `
UPDATE etapi_tokens SET tokenHash = 'API token hash value'; UPDATE etapi_tokens SET tokenHash = 'API token hash value';
UPDATE notes SET title = 'title' WHERE title NOT IN ('root', '_hidden', '_share'); UPDATE notes SET title = 'title' WHERE title NOT IN ('root', '_hidden', '_share');
UPDATE note_contents SET content = 'text' WHERE content IS NOT NULL; UPDATE blobs SET content = 'text' WHERE content IS NOT NULL;
UPDATE note_revisions SET title = 'title'; UPDATE note_revisions SET title = 'title';
UPDATE note_revision_contents SET content = 'text' WHERE content IS NOT NULL;
UPDATE attributes SET name = 'name', value = 'value' WHERE type = 'label' AND name NOT IN(${builtinAttrNames}); UPDATE attributes SET name = 'name', value = 'value' WHERE type = 'label' AND name NOT IN(${builtinAttrNames});
UPDATE attributes SET name = 'name' WHERE type = 'relation' AND name NOT IN (${builtinAttrNames}); UPDATE attributes SET name = 'name' WHERE type = 'relation' AND name NOT IN (${builtinAttrNames});
@ -34,14 +33,11 @@ VACUUM;
} }
function getLightAnonymizationScript() { function getLightAnonymizationScript() {
return ` return `UPDATE blobs SET content = 'text' WHERE content IS NOT NULL AND blobId NOT IN (
UPDATE note_contents SET content = 'text' WHERE content IS NOT NULL AND noteId NOT IN ( SELECT blobId FROM notes WHERE mime IN ('application/javascript;env=backend', 'application/javascript;env=frontend')
SELECT noteId FROM notes WHERE mime IN ('application/javascript;env=backend', 'application/javascript;env=frontend') UNION ALL
); SELECT blobId FROM note_revisions WHERE mime IN ('application/javascript;env=backend', 'application/javascript;env=frontend')
UPDATE note_revision_contents SET content = 'text' WHERE content IS NOT NULL AND noteRevisionId NOT IN ( );`;
SELECT noteRevisionId FROM note_revisions WHERE mime IN ('application/javascript;env=backend', 'application/javascript;env=frontend')
);
`;
} }
async function createAnonymizedCopy(type) { async function createAnonymizedCopy(type) {

View File

@ -656,11 +656,9 @@ class ConsistencyChecks {
findEntityChangeIssues() { findEntityChangeIssues() {
this.runEntityChangeChecks("notes", "noteId"); this.runEntityChangeChecks("notes", "noteId");
//this.runEntityChangeChecks("note_contents", "noteId");
this.runEntityChangeChecks("note_revisions", "noteRevisionId"); this.runEntityChangeChecks("note_revisions", "noteRevisionId");
//this.runEntityChangeChecks("note_revision_contents", "noteRevisionId");
this.runEntityChangeChecks("note_ancillaries", "noteAncillaryId"); this.runEntityChangeChecks("note_ancillaries", "noteAncillaryId");
//this.runEntityChangeChecks("note_ancillary_contents", "noteAncillaryId"); this.runEntityChangeChecks("blobs", "blobId");
this.runEntityChangeChecks("branches", "branchId"); this.runEntityChangeChecks("branches", "branchId");
this.runEntityChangeChecks("attributes", "attributeId"); this.runEntityChangeChecks("attributes", "attributeId");
this.runEntityChangeChecks("etapi_tokens", "etapiTokenId"); this.runEntityChangeChecks("etapi_tokens", "etapiTokenId");

View File

@ -104,7 +104,7 @@ function fillEntityChanges(entityName, entityPrimaryKey, condition = '') {
let utcDateChanged; let utcDateChanged;
let isSynced; let isSynced;
if (entityName.endsWith("_contents")) { if (entityName === 'blobs') {
// FIXME: hacky, not sure if it might cause some problems // FIXME: hacky, not sure if it might cause some problems
hash = "fake value"; hash = "fake value";
utcDateChanged = dateUtils.utcNowDateTime(); utcDateChanged = dateUtils.utcNowDateTime();
@ -147,12 +147,10 @@ function fillAllEntityChanges() {
sql.execute("DELETE FROM entity_changes WHERE isErased = 0"); sql.execute("DELETE FROM entity_changes WHERE isErased = 0");
fillEntityChanges("notes", "noteId"); fillEntityChanges("notes", "noteId");
fillEntityChanges("note_contents", "noteId");
fillEntityChanges("branches", "branchId"); fillEntityChanges("branches", "branchId");
fillEntityChanges("note_revisions", "noteRevisionId"); fillEntityChanges("note_revisions", "noteRevisionId");
fillEntityChanges("note_revision_contents", "noteRevisionId");
fillEntityChanges("note_ancillaries", "noteAncillaryId"); fillEntityChanges("note_ancillaries", "noteAncillaryId");
fillEntityChanges("note_ancillary_contents", "noteAncillaryId"); fillEntityChanges("blobs", "blobId");
fillEntityChanges("attributes", "attributeId"); fillEntityChanges("attributes", "attributeId");
fillEntityChanges("etapi_tokens", "etapiTokenId"); fillEntityChanges("etapi_tokens", "etapiTokenId");
fillEntityChanges("options", "name", 'isSynced = 1'); fillEntityChanges("options", "name", 'isSynced = 1');

View File

@ -59,7 +59,7 @@ eventService.subscribe([ eventService.ENTITY_CHANGED, eventService.ENTITY_DELETE
}); });
eventService.subscribe(eventService.ENTITY_CHANGED, ({entityName, entity}) => { eventService.subscribe(eventService.ENTITY_CHANGED, ({entityName, entity}) => {
if (entityName === 'note_contents') { if (entityName === 'note_contents') { // FIXME
runAttachedRelations(entity, 'runOnNoteContentChange', entity); runAttachedRelations(entity, 'runOnNoteContentChange', entity);
} }
}); });

View File

@ -206,7 +206,7 @@ function importEnex(taskContext, file, parentNote) {
} }
}); });
function updateDates(noteId, utcDateCreated, utcDateModified) { function updateDates(note, utcDateCreated, utcDateModified) {
// it's difficult to force custom dateCreated and dateModified to Note entity, so we do it post-creation with SQL // it's difficult to force custom dateCreated and dateModified to Note entity, so we do it post-creation with SQL
sql.execute(` sql.execute(`
UPDATE notes UPDATE notes
@ -215,13 +215,13 @@ function importEnex(taskContext, file, parentNote) {
dateModified = ?, dateModified = ?,
utcDateModified = ? utcDateModified = ?
WHERE noteId = ?`, WHERE noteId = ?`,
[utcDateCreated, utcDateCreated, utcDateModified, utcDateModified, noteId]); [utcDateCreated, utcDateCreated, utcDateModified, utcDateModified, note.noteId]);
sql.execute(` sql.execute(`
UPDATE note_contents UPDATE blobs
SET utcDateModified = ? SET utcDateModified = ?
WHERE noteId = ?`, WHERE blobId = ?`,
[utcDateModified, noteId]); [utcDateModified, note.blobId]);
} }
function saveNote() { function saveNote() {
@ -287,7 +287,7 @@ function importEnex(taskContext, file, parentNote) {
resourceNote.addAttribute(attr.type, attr.name, attr.value); resourceNote.addAttribute(attr.type, attr.name, attr.value);
} }
updateDates(resourceNote.noteId, utcDateCreated, utcDateModified); updateDates(resourceNote, utcDateCreated, utcDateModified);
taskContext.increaseProgressCount(); taskContext.increaseProgressCount();
@ -310,7 +310,7 @@ function importEnex(taskContext, file, parentNote) {
} }
} }
updateDates(imageNote.noteId, utcDateCreated, utcDateModified); updateDates(imageNote, utcDateCreated, utcDateModified);
const imageLink = `<img src="${url}">`; const imageLink = `<img src="${url}">`;
@ -337,7 +337,7 @@ function importEnex(taskContext, file, parentNote) {
noteService.asyncPostProcessContent(noteEntity, content); noteService.asyncPostProcessContent(noteEntity, content);
updateDates(noteEntity.noteId, utcDateCreated, utcDateModified); updateDates(noteEntity, utcDateCreated, utcDateModified);
} }
saxStream.on("closetag", tag => { saxStream.on("closetag", tag => {

View File

@ -14,7 +14,7 @@ const treeService = require("../tree");
const yauzl = require("yauzl"); const yauzl = require("yauzl");
const htmlSanitizer = require('../html_sanitizer'); const htmlSanitizer = require('../html_sanitizer');
const becca = require("../../becca/becca"); const becca = require("../../becca/becca");
const BNoteAncillary = require("../../becca/entities/bnote_ancillary"); const BNoteAncillary = require("../../becca/entities/bnote_attachment.js");
/** /**
* @param {TaskContext} taskContext * @param {TaskContext} taskContext

View File

@ -44,9 +44,6 @@ function eraseNoteRevisions(noteRevisionIdsToErase) {
sql.executeMany(`DELETE FROM note_revisions WHERE noteRevisionId IN (???)`, noteRevisionIdsToErase); sql.executeMany(`DELETE FROM note_revisions WHERE noteRevisionId IN (???)`, noteRevisionIdsToErase);
sql.executeMany(`UPDATE entity_changes SET isErased = 1 WHERE entityName = 'note_revisions' AND entityId IN (???)`, noteRevisionIdsToErase); sql.executeMany(`UPDATE entity_changes SET isErased = 1 WHERE entityName = 'note_revisions' AND entityId IN (???)`, noteRevisionIdsToErase);
sql.executeMany(`DELETE FROM note_revision_contents WHERE noteRevisionId IN (???)`, noteRevisionIdsToErase);
sql.executeMany(`UPDATE entity_changes SET isErased = 1 WHERE entityName = 'note_revision_contents' AND entityId IN (???)`, noteRevisionIdsToErase);
} }
module.exports = { module.exports = {

View File

@ -220,7 +220,7 @@ function createNewNote(params) {
entity: note entity: note
}); });
eventService.emit(eventService.ENTITY_CREATED, { eventService.emit(eventService.ENTITY_CREATED, { // FIXME
entityName: 'note_contents', entityName: 'note_contents',
entity: note entity: note
}); });
@ -499,7 +499,7 @@ function downloadImages(noteId, content) {
asyncPostProcessContent(origNote, updatedContent); asyncPostProcessContent(origNote, updatedContent);
eventService.emit(eventService.ENTITY_CHANGED, { eventService.emit(eventService.ENTITY_CHANGED, {
entityName: 'note_contents', entityName: 'note_contents', // FIXME
entity: origNote entity: origNote
}); });
@ -733,9 +733,6 @@ function eraseNotes(noteIdsToErase) {
sql.executeMany(`DELETE FROM notes WHERE noteId IN (???)`, noteIdsToErase); sql.executeMany(`DELETE FROM notes WHERE noteId IN (???)`, noteIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'notes' AND entityId IN (???)`, noteIdsToErase)); setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'notes' AND entityId IN (???)`, noteIdsToErase));
sql.executeMany(`DELETE FROM note_contents WHERE noteId IN (???)`, noteIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'note_contents' AND entityId IN (???)`, noteIdsToErase));
// we also need to erase all "dependent" entities of the erased notes // we also need to erase all "dependent" entities of the erased notes
const branchIdsToErase = sql.getManyRows(`SELECT branchId FROM branches WHERE noteId IN (???)`, noteIdsToErase) const branchIdsToErase = sql.getManyRows(`SELECT branchId FROM branches WHERE noteId IN (???)`, noteIdsToErase)
.map(row => row.branchId); .map(row => row.branchId);

View File

@ -42,7 +42,7 @@ class NoteContentFulltextExp extends Expression {
for (const row of sql.iterateRows(` for (const row of sql.iterateRows(`
SELECT noteId, type, mime, content, isProtected SELECT noteId, type, mime, content, isProtected
FROM notes JOIN note_contents USING (noteId) FROM notes JOIN blobs USING (blobId)
WHERE type IN ('text', 'code', 'mermaid') AND isDeleted = 0`)) { WHERE type IN ('text', 'code', 'mermaid') AND isDeleted = 0`)) {
this.findInText(row, inputNoteSet, resultNoteSet); this.findInText(row, inputNoteSet, resultNoteSet);

View File

@ -103,7 +103,7 @@ function loadNeededInfoFromDatabase() {
noteId, noteId,
LENGTH(content) AS length LENGTH(content) AS length
FROM notes FROM notes
JOIN note_contents USING(noteId) JOIN blobs USING(blobId)
WHERE notes.isDeleted = 0`); WHERE notes.isDeleted = 0`);
for (const {noteId, length} of noteContentLengths) { for (const {noteId, length} of noteContentLengths) {
@ -122,7 +122,7 @@ function loadNeededInfoFromDatabase() {
LENGTH(content) AS length LENGTH(content) AS length
FROM notes FROM notes
JOIN note_revisions USING(noteId) JOIN note_revisions USING(noteId)
JOIN note_revision_contents USING(noteRevisionId) JOIN blobs USING(blobId)
WHERE notes.isDeleted = 0`); WHERE notes.isDeleted = 0`);
for (const {noteId, length} of noteRevisionContentLengths) { for (const {noteId, length} of noteRevisionContentLengths) {

View File

@ -321,7 +321,7 @@ function getEntityChangeRow(entityName, entityId) {
throw new Error(`Entity ${entityName} ${entityId} not found.`); throw new Error(`Entity ${entityName} ${entityId} not found.`);
} }
if (['note_contents', 'note_revision_contents', 'note_ancillary_contents'].includes(entityName) && entity.content !== null) { if (entityName === 'blobs' && entity.content !== null) {
if (typeof entity.content === 'string') { if (typeof entity.content === 'string') {
entity.content = Buffer.from(entity.content, 'UTF-8'); entity.content = Buffer.from(entity.content, 'UTF-8');
} }

View File

@ -64,7 +64,7 @@ function updateNormalEntity(remoteEntityChange, remoteEntityRow, instanceId) {
|| localEntityChange.utcDateChanged < remoteEntityChange.utcDateChanged || localEntityChange.utcDateChanged < remoteEntityChange.utcDateChanged
|| localEntityChange.hash !== remoteEntityChange.hash // sync error, we should still update || localEntityChange.hash !== remoteEntityChange.hash // sync error, we should still update
) { ) {
if (['note_contents', 'note_revision_contents', 'note_ancillary_contents'].includes(remoteEntityChange.entityName)) { if (remoteEntityChange.entityName === 'blobs') {
remoteEntityRow.content = handleContent(remoteEntityRow.content); remoteEntityRow.content = handleContent(remoteEntityRow.content);
} }
@ -94,7 +94,7 @@ function updateNoteReordering(entityChange, entity, instanceId) {
function handleContent(content) { function handleContent(content) {
// we always use Buffer object which is different from normal saving - there we use simple string type for // we always use Buffer object which is different from normal saving - there we use simple string type for
// "string notes". The problem is that in general it's not possible to detect whether a note_content // "string notes". The problem is that in general it's not possible to detect whether a blob content
// is string note or note (syncs can arrive out of order) // is string note or note (syncs can arrive out of order)
content = content === null ? null : Buffer.from(content, 'base64'); content = content === null ? null : Buffer.from(content, 'base64');
@ -111,13 +111,11 @@ function eraseEntity(entityChange, instanceId) {
const entityNames = [ const entityNames = [
"notes", "notes",
"note_contents",
"branches", "branches",
"attributes", "attributes",
"note_revisions", "note_revisions",
"note_revision_contents",
"note_ancillaries", "note_ancillaries",
"note_ancillary_contents" "blobs",
]; ];
if (!entityNames.includes(entityName)) { if (!entityNames.includes(entityName)) {

View File

@ -147,13 +147,13 @@ function fillInAdditionalProperties(entityChange) {
// entities with higher number can reference the entities with lower number // entities with higher number can reference the entities with lower number
const ORDERING = { const ORDERING = {
"etapi_tokens": 0, "etapi_tokens": 0,
"attributes": 1, "attributes": 2,
"branches": 1, "branches": 2,
"note_contents": 1, "blobs": 0,
"note_reordering": 1, "note_reordering": 2,
"note_revision_contents": 2, "note_revisions": 2,
"note_revisions": 1, "note_attachments": 3,
"notes": 0, "notes": 1,
"options": 0 "options": 0
}; };

View File

@ -12,7 +12,7 @@ const CREDENTIALS = 'shareCredentials';
const isCredentials = attr => attr.type === 'label' && attr.name === CREDENTIALS; const isCredentials = attr => attr.type === 'label' && attr.name === CREDENTIALS;
class SNote extends AbstractShacaEntity { class SNote extends AbstractShacaEntity {
constructor([noteId, title, type, mime, utcDateModified, isProtected]) { constructor([noteId, title, type, mime, blobId, utcDateModified, isProtected]) {
super(); super();
/** @param {string} */ /** @param {string} */
@ -24,6 +24,8 @@ class SNote extends AbstractShacaEntity {
/** @param {string} */ /** @param {string} */
this.mime = mime; this.mime = mime;
/** @param {string} */ /** @param {string} */
this.blobId = blobId;
/** @param {string} */
this.utcDateModified = utcDateModified; // used for caching of images this.utcDateModified = utcDateModified; // used for caching of images
/** @param {boolean} */ /** @param {boolean} */
this.isProtected = isProtected; this.isProtected = isProtected;
@ -92,14 +94,14 @@ class SNote extends AbstractShacaEntity {
} }
getContent(silentNotFoundError = false) { getContent(silentNotFoundError = false) {
const row = sql.getRow(`SELECT content FROM note_contents WHERE noteId = ?`, [this.noteId]); const row = sql.getRow(`SELECT content FROM blobs WHERE blobId = ?`, [this.blobId]);
if (!row) { if (!row) {
if (silentNotFoundError) { if (silentNotFoundError) {
return undefined; return undefined;
} }
else { else {
throw new Error(`Cannot find note content for noteId=${this.noteId}`); throw new Error(`Cannot find note content for noteId '${this.noteId}', blobId '${this.blobId}'`);
} }
} }

View File

@ -35,7 +35,7 @@ function load() {
const noteIdStr = noteIds.map(noteId => `'${noteId}'`).join(","); const noteIdStr = noteIds.map(noteId => `'${noteId}'`).join(",");
const rawNoteRows = sql.getRawRows(` const rawNoteRows = sql.getRawRows(`
SELECT noteId, title, type, mime, utcDateModified, isProtected SELECT noteId, title, type, mime, blobId, utcDateModified, isProtected
FROM notes FROM notes
WHERE isDeleted = 0 WHERE isDeleted = 0
AND noteId IN (${noteIdStr})`); AND noteId IN (${noteIdStr})`);