2021-01-30 22:17:29 +01:00
|
|
|
CREATE TABLE IF NOT EXISTS "entity_changes" (
|
|
|
|
|
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
`entityName` TEXT NOT NULL,
|
|
|
|
|
`entityId` TEXT NOT NULL,
|
|
|
|
|
`hash` TEXT NOT NULL,
|
|
|
|
|
`isErased` INT NOT NULL,
|
2021-11-18 22:33:08 +01:00
|
|
|
`changeId` TEXT NOT NULL,
|
2022-01-09 20:16:39 +01:00
|
|
|
`componentId` TEXT NOT NULL,
|
2022-01-09 21:25:15 +01:00
|
|
|
`instanceId` TEXT NOT NULL,
|
2021-01-30 22:17:29 +01:00
|
|
|
`isSynced` INTEGER NOT NULL,
|
|
|
|
|
`utcDateChanged` TEXT NOT NULL
|
|
|
|
|
);
|
2022-01-10 17:09:20 +01:00
|
|
|
CREATE TABLE IF NOT EXISTS "etapi_tokens"
|
2018-01-11 00:01:16 -05:00
|
|
|
(
|
2022-01-10 17:09:20 +01:00
|
|
|
etapiTokenId TEXT PRIMARY KEY NOT NULL,
|
2022-01-07 19:33:59 +01:00
|
|
|
name TEXT NOT NULL,
|
2022-01-10 17:09:20 +01:00
|
|
|
tokenHash TEXT NOT NULL,
|
2021-01-30 22:17:29 +01:00
|
|
|
utcDateCreated TEXT NOT NULL,
|
2022-01-10 21:24:07 +01:00
|
|
|
utcDateModified TEXT NOT NULL,
|
2021-01-30 22:17:29 +01:00
|
|
|
isDeleted INT NOT NULL DEFAULT 0);
|
2020-01-03 10:48:36 +01:00
|
|
|
CREATE TABLE IF NOT EXISTS "branches" (
|
|
|
|
|
`branchId` TEXT NOT NULL,
|
|
|
|
|
`noteId` TEXT NOT NULL,
|
|
|
|
|
`parentNoteId` TEXT NOT NULL,
|
|
|
|
|
`notePosition` INTEGER NOT NULL,
|
|
|
|
|
`prefix` TEXT,
|
|
|
|
|
`isExpanded` INTEGER NOT NULL DEFAULT 0,
|
|
|
|
|
`isDeleted` INTEGER NOT NULL DEFAULT 0,
|
|
|
|
|
`deleteId` TEXT DEFAULT NULL,
|
2021-11-21 13:44:52 +01:00
|
|
|
`utcDateModified` TEXT NOT NULL,
|
2020-01-03 10:48:36 +01:00
|
|
|
PRIMARY KEY(`branchId`));
|
2020-08-18 22:37:23 +02:00
|
|
|
CREATE TABLE IF NOT EXISTS "notes" (
|
|
|
|
|
`noteId` TEXT NOT NULL,
|
|
|
|
|
`title` TEXT NOT NULL DEFAULT "note",
|
|
|
|
|
`isProtected` INT NOT NULL DEFAULT 0,
|
|
|
|
|
`type` TEXT NOT NULL DEFAULT 'text',
|
|
|
|
|
`mime` TEXT NOT NULL DEFAULT 'text/html',
|
2023-06-04 23:01:40 +02:00
|
|
|
blobId TEXT DEFAULT NULL,
|
2020-08-18 22:37:23 +02:00
|
|
|
`isDeleted` INT NOT NULL DEFAULT 0,
|
|
|
|
|
`deleteId` TEXT DEFAULT NULL,
|
|
|
|
|
`dateCreated` TEXT NOT NULL,
|
|
|
|
|
`dateModified` TEXT NOT NULL,
|
|
|
|
|
`utcDateCreated` TEXT NOT NULL,
|
2023-06-04 23:01:40 +02:00
|
|
|
`utcDateModified` TEXT NOT NULL,
|
2020-08-18 22:37:23 +02:00
|
|
|
PRIMARY KEY(`noteId`));
|
2023-06-04 23:01:40 +02:00
|
|
|
CREATE TABLE IF NOT EXISTS "revisions" (`revisionId` TEXT NOT NULL PRIMARY KEY,
|
2020-08-18 22:37:23 +02:00
|
|
|
`noteId` TEXT NOT NULL,
|
2020-12-15 15:30:46 +01:00
|
|
|
type TEXT DEFAULT '' NOT NULL,
|
|
|
|
|
mime TEXT DEFAULT '' NOT NULL,
|
2022-01-12 19:32:23 +01:00
|
|
|
`title` TEXT NOT NULL,
|
2020-08-18 22:37:23 +02:00
|
|
|
`isProtected` INT NOT NULL DEFAULT 0,
|
2023-06-04 23:01:40 +02:00
|
|
|
blobId TEXT DEFAULT NULL,
|
2020-08-18 22:37:23 +02:00
|
|
|
`utcDateLastEdited` TEXT NOT NULL,
|
|
|
|
|
`utcDateCreated` TEXT NOT NULL,
|
|
|
|
|
`utcDateModified` TEXT NOT NULL,
|
|
|
|
|
`dateLastEdited` TEXT NOT NULL,
|
2023-06-04 23:01:40 +02:00
|
|
|
`dateCreated` TEXT NOT NULL);
|
2020-12-15 15:30:46 +01:00
|
|
|
CREATE TABLE IF NOT EXISTS "options"
|
|
|
|
|
(
|
|
|
|
|
name TEXT not null PRIMARY KEY,
|
2022-01-12 19:32:23 +01:00
|
|
|
value TEXT not null,
|
2020-12-15 15:30:46 +01:00
|
|
|
isSynced INTEGER default 0 not null,
|
|
|
|
|
utcDateModified TEXT NOT NULL
|
2020-08-18 22:37:23 +02:00
|
|
|
);
|
2020-12-15 15:30:46 +01:00
|
|
|
CREATE TABLE IF NOT EXISTS "attributes"
|
|
|
|
|
(
|
|
|
|
|
attributeId TEXT not null primary key,
|
|
|
|
|
noteId TEXT not null,
|
|
|
|
|
type TEXT not null,
|
|
|
|
|
name TEXT not null,
|
|
|
|
|
value TEXT default '' not null,
|
|
|
|
|
position INT default 0 not null,
|
|
|
|
|
utcDateModified TEXT not null,
|
|
|
|
|
isDeleted INT not null,
|
|
|
|
|
`deleteId` TEXT DEFAULT NULL,
|
|
|
|
|
isInheritable int DEFAULT 0 NULL);
|
2021-01-30 22:17:29 +01:00
|
|
|
CREATE UNIQUE INDEX `IDX_entityChanges_entityName_entityId` ON "entity_changes" (
|
|
|
|
|
`entityName`,
|
|
|
|
|
`entityId`
|
|
|
|
|
);
|
|
|
|
|
CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` (`noteId`,`parentNoteId`);
|
|
|
|
|
CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId);
|
|
|
|
|
CREATE INDEX `IDX_notes_title` ON `notes` (`title`);
|
|
|
|
|
CREATE INDEX `IDX_notes_type` ON `notes` (`type`);
|
|
|
|
|
CREATE INDEX `IDX_notes_dateCreated` ON `notes` (`dateCreated`);
|
|
|
|
|
CREATE INDEX `IDX_notes_dateModified` ON `notes` (`dateModified`);
|
|
|
|
|
CREATE INDEX `IDX_notes_utcDateModified` ON `notes` (`utcDateModified`);
|
|
|
|
|
CREATE INDEX `IDX_notes_utcDateCreated` ON `notes` (`utcDateCreated`);
|
2023-06-04 23:01:40 +02:00
|
|
|
CREATE INDEX `IDX_revisions_noteId` ON `revisions` (`noteId`);
|
|
|
|
|
CREATE INDEX `IDX_revisions_utcDateCreated` ON `revisions` (`utcDateCreated`);
|
|
|
|
|
CREATE INDEX `IDX_revisions_utcDateLastEdited` ON `revisions` (`utcDateLastEdited`);
|
|
|
|
|
CREATE INDEX `IDX_revisions_dateCreated` ON `revisions` (`dateCreated`);
|
|
|
|
|
CREATE INDEX `IDX_revisions_dateLastEdited` ON `revisions` (`dateLastEdited`);
|
2022-01-09 21:25:15 +01:00
|
|
|
CREATE INDEX `IDX_entity_changes_changeId` ON `entity_changes` (`changeId`);
|
2020-12-10 22:53:52 +01:00
|
|
|
CREATE INDEX IDX_attributes_name_value
|
|
|
|
|
on attributes (name, value);
|
|
|
|
|
CREATE INDEX IDX_attributes_noteId_index
|
|
|
|
|
on attributes (noteId);
|
|
|
|
|
CREATE INDEX IDX_attributes_value_index
|
|
|
|
|
on attributes (value);
|
2021-02-12 23:30:11 +01:00
|
|
|
CREATE TABLE IF NOT EXISTS "recent_notes"
|
|
|
|
|
(
|
|
|
|
|
noteId TEXT not null primary key,
|
|
|
|
|
notePath TEXT not null,
|
|
|
|
|
utcDateCreated TEXT not null
|
|
|
|
|
);
|
2023-04-01 23:55:04 +02:00
|
|
|
CREATE TABLE IF NOT EXISTS "blobs" (
|
|
|
|
|
`blobId` TEXT NOT NULL,
|
|
|
|
|
`content` TEXT NULL DEFAULT NULL,
|
|
|
|
|
`dateModified` TEXT NOT NULL,
|
|
|
|
|
`utcDateModified` TEXT NOT NULL,
|
|
|
|
|
PRIMARY KEY(`blobId`)
|
|
|
|
|
);
|
2023-03-16 12:17:55 +01:00
|
|
|
CREATE TABLE IF NOT EXISTS "attachments"
|
2023-03-08 09:01:23 +01:00
|
|
|
(
|
2023-03-16 12:17:55 +01:00
|
|
|
attachmentId TEXT not null primary key,
|
2023-07-14 17:01:56 +02:00
|
|
|
ownerId TEXT not null,
|
2023-03-16 12:17:55 +01:00
|
|
|
role TEXT not null,
|
2023-03-08 09:01:23 +01:00
|
|
|
mime TEXT not null,
|
2023-03-16 12:17:55 +01:00
|
|
|
title TEXT not null,
|
2023-03-08 09:01:23 +01:00
|
|
|
isProtected INT not null DEFAULT 0,
|
2023-04-11 22:55:50 +02:00
|
|
|
position INT default 0 not null,
|
2023-03-16 18:34:39 +01:00
|
|
|
blobId TEXT DEFAULT null,
|
2023-04-03 23:47:24 +02:00
|
|
|
dateModified TEXT NOT NULL,
|
2023-03-08 09:01:23 +01:00
|
|
|
utcDateModified TEXT not null,
|
2023-04-21 00:19:17 +02:00
|
|
|
utcDateScheduledForErasureSince TEXT DEFAULT NULL,
|
2023-03-08 09:01:23 +01:00
|
|
|
isDeleted INT not null,
|
2023-03-16 12:17:55 +01:00
|
|
|
deleteId TEXT DEFAULT NULL);
|
2024-09-07 10:21:41 -07:00
|
|
|
CREATE TABLE IF NOT EXISTS "user_data"
|
|
|
|
|
(
|
|
|
|
|
tmpID INT,
|
2024-09-07 13:18:47 -07:00
|
|
|
username TEXT,
|
|
|
|
|
email TEXT,
|
2025-03-29 01:00:08 +01:00
|
|
|
userIDEncryptedDataKey TEXT,
|
2024-09-07 10:21:41 -07:00
|
|
|
userIDVerificationHash TEXT,
|
|
|
|
|
salt TEXT,
|
|
|
|
|
derivedKey TEXT,
|
|
|
|
|
isSetup TEXT DEFAULT "false",
|
|
|
|
|
UNIQUE (tmpID),
|
|
|
|
|
PRIMARY KEY (tmpID)
|
|
|
|
|
);
|
2023-07-14 17:01:56 +02:00
|
|
|
CREATE INDEX IDX_attachments_ownerId_role
|
|
|
|
|
on attachments (ownerId, role);
|
2023-07-27 21:40:47 +02:00
|
|
|
|
|
|
|
|
CREATE INDEX IDX_notes_blobId on notes (blobId);
|
|
|
|
|
CREATE INDEX IDX_revisions_blobId on revisions (blobId);
|
|
|
|
|
CREATE INDEX IDX_attachments_blobId on attachments (blobId);
|
2025-03-08 23:01:45 +00:00
|
|
|
|
2025-08-30 18:26:31 +00:00
|
|
|
-- Strategic Performance Indexes from migration 234
|
|
|
|
|
-- NOTES TABLE INDEXES
|
|
|
|
|
CREATE INDEX IDX_notes_search_composite
|
|
|
|
|
ON notes (isDeleted, type, mime, dateModified DESC);
|
|
|
|
|
|
|
|
|
|
CREATE INDEX IDX_notes_metadata_covering
|
|
|
|
|
ON notes (noteId, isDeleted, type, mime, title, dateModified, isProtected);
|
|
|
|
|
|
|
|
|
|
CREATE INDEX IDX_notes_protected_deleted
|
|
|
|
|
ON notes (isProtected, isDeleted)
|
|
|
|
|
WHERE isProtected = 1;
|
|
|
|
|
|
|
|
|
|
-- BRANCHES TABLE INDEXES
|
|
|
|
|
CREATE INDEX IDX_branches_tree_traversal
|
|
|
|
|
ON branches (parentNoteId, isDeleted, notePosition);
|
|
|
|
|
|
|
|
|
|
CREATE INDEX IDX_branches_covering
|
|
|
|
|
ON branches (noteId, parentNoteId, isDeleted, notePosition, prefix);
|
|
|
|
|
|
|
|
|
|
CREATE INDEX IDX_branches_note_parents
|
|
|
|
|
ON branches (noteId, isDeleted)
|
|
|
|
|
WHERE isDeleted = 0;
|
|
|
|
|
|
|
|
|
|
-- ATTRIBUTES TABLE INDEXES
|
|
|
|
|
CREATE INDEX IDX_attributes_search_composite
|
|
|
|
|
ON attributes (name, value, isDeleted);
|
|
|
|
|
|
|
|
|
|
CREATE INDEX IDX_attributes_covering
|
|
|
|
|
ON attributes (noteId, name, value, type, isDeleted, position);
|
|
|
|
|
|
|
|
|
|
CREATE INDEX IDX_attributes_inheritable
|
|
|
|
|
ON attributes (isInheritable, isDeleted)
|
|
|
|
|
WHERE isInheritable = 1 AND isDeleted = 0;
|
|
|
|
|
|
|
|
|
|
CREATE INDEX IDX_attributes_labels
|
|
|
|
|
ON attributes (type, name, value)
|
|
|
|
|
WHERE type = 'label' AND isDeleted = 0;
|
|
|
|
|
|
|
|
|
|
CREATE INDEX IDX_attributes_relations
|
|
|
|
|
ON attributes (type, name, value)
|
|
|
|
|
WHERE type = 'relation' AND isDeleted = 0;
|
|
|
|
|
|
|
|
|
|
-- BLOBS TABLE INDEXES
|
|
|
|
|
CREATE INDEX IDX_blobs_content_size
|
|
|
|
|
ON blobs (blobId, LENGTH(content));
|
|
|
|
|
|
|
|
|
|
-- ATTACHMENTS TABLE INDEXES
|
|
|
|
|
CREATE INDEX IDX_attachments_composite
|
|
|
|
|
ON attachments (ownerId, role, isDeleted, position);
|
|
|
|
|
|
|
|
|
|
-- REVISIONS TABLE INDEXES
|
|
|
|
|
CREATE INDEX IDX_revisions_note_date
|
|
|
|
|
ON revisions (noteId, utcDateCreated DESC);
|
|
|
|
|
|
|
|
|
|
-- ENTITY_CHANGES TABLE INDEXES
|
|
|
|
|
CREATE INDEX IDX_entity_changes_sync
|
|
|
|
|
ON entity_changes (isSynced, utcDateChanged);
|
|
|
|
|
|
|
|
|
|
CREATE INDEX IDX_entity_changes_component
|
|
|
|
|
ON entity_changes (componentId, utcDateChanged DESC);
|
|
|
|
|
|
|
|
|
|
-- RECENT_NOTES TABLE INDEXES
|
|
|
|
|
CREATE INDEX IDX_recent_notes_date
|
|
|
|
|
ON recent_notes (utcDateCreated DESC);
|
|
|
|
|
|
2025-05-16 23:07:23 +03:00
|
|
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
|
|
|
id TEXT PRIMARY KEY,
|
|
|
|
|
data TEXT,
|
|
|
|
|
expires INTEGER
|
|
|
|
|
);
|
2025-08-30 18:26:31 +00:00
|
|
|
|
|
|
|
|
-- FTS5 Full-Text Search Support
|
2025-09-02 19:24:50 +00:00
|
|
|
-- Create FTS5 virtual table for full-text searching
|
2025-08-30 18:26:31 +00:00
|
|
|
CREATE VIRTUAL TABLE notes_fts USING fts5(
|
|
|
|
|
noteId UNINDEXED,
|
|
|
|
|
title,
|
|
|
|
|
content,
|
2025-09-02 19:24:46 +00:00
|
|
|
tokenize = 'porter unicode61'
|
2025-08-30 18:26:31 +00:00
|
|
|
);
|
|
|
|
|
|
2025-09-02 19:24:46 +00:00
|
|
|
-- Triggers to keep FTS table synchronized with notes
|
|
|
|
|
-- IMPORTANT: These triggers must handle all SQL operations including:
|
|
|
|
|
-- - Regular INSERT/UPDATE/DELETE
|
|
|
|
|
-- - INSERT OR REPLACE
|
|
|
|
|
-- - INSERT ... ON CONFLICT ... DO UPDATE (upsert)
|
|
|
|
|
-- - Cases where notes are created before blobs (import scenarios)
|
2025-08-30 18:26:31 +00:00
|
|
|
|
2025-09-02 19:24:46 +00:00
|
|
|
-- Trigger for INSERT operations on notes
|
|
|
|
|
-- Handles: INSERT, INSERT OR REPLACE, INSERT OR IGNORE, and the INSERT part of upsert
|
2025-08-30 18:26:31 +00:00
|
|
|
CREATE TRIGGER notes_fts_insert
|
|
|
|
|
AFTER INSERT ON notes
|
|
|
|
|
WHEN NEW.type IN ('text', 'code', 'mermaid', 'canvas', 'mindMap')
|
|
|
|
|
AND NEW.isDeleted = 0
|
|
|
|
|
AND NEW.isProtected = 0
|
|
|
|
|
BEGIN
|
2025-09-02 19:24:50 +00:00
|
|
|
-- First delete any existing FTS entry (in case of INSERT OR REPLACE)
|
2025-09-02 19:24:46 +00:00
|
|
|
DELETE FROM notes_fts WHERE noteId = NEW.noteId;
|
|
|
|
|
|
2025-09-02 19:24:50 +00:00
|
|
|
-- Then insert the new entry, using LEFT JOIN to handle missing blobs
|
2025-09-02 19:24:46 +00:00
|
|
|
INSERT INTO notes_fts (noteId, title, content)
|
|
|
|
|
SELECT
|
|
|
|
|
NEW.noteId,
|
|
|
|
|
NEW.title,
|
|
|
|
|
COALESCE(b.content, '') -- Use empty string if blob doesn't exist yet
|
|
|
|
|
FROM (SELECT NEW.noteId) AS note_select
|
|
|
|
|
LEFT JOIN blobs b ON b.blobId = NEW.blobId;
|
2025-08-30 18:26:31 +00:00
|
|
|
END;
|
|
|
|
|
|
2025-09-02 19:24:46 +00:00
|
|
|
-- Trigger for UPDATE operations on notes table
|
|
|
|
|
-- Handles: Regular UPDATE and the UPDATE part of upsert (ON CONFLICT DO UPDATE)
|
|
|
|
|
-- Fires for ANY update to searchable notes to ensure FTS stays in sync
|
2025-08-30 18:26:31 +00:00
|
|
|
CREATE TRIGGER notes_fts_update
|
|
|
|
|
AFTER UPDATE ON notes
|
2025-09-02 19:24:46 +00:00
|
|
|
WHEN NEW.type IN ('text', 'code', 'mermaid', 'canvas', 'mindMap')
|
|
|
|
|
-- Fire on any change, not just specific columns, to handle all upsert scenarios
|
2025-08-30 18:26:31 +00:00
|
|
|
BEGIN
|
2025-09-02 19:24:50 +00:00
|
|
|
-- Always delete the old entry
|
2025-08-30 18:26:31 +00:00
|
|
|
DELETE FROM notes_fts WHERE noteId = NEW.noteId;
|
|
|
|
|
|
2025-09-02 19:24:50 +00:00
|
|
|
-- Insert new entry if note is not deleted and not protected
|
2025-09-02 19:24:46 +00:00
|
|
|
INSERT INTO notes_fts (noteId, title, content)
|
|
|
|
|
SELECT
|
|
|
|
|
NEW.noteId,
|
|
|
|
|
NEW.title,
|
|
|
|
|
COALESCE(b.content, '') -- Use empty string if blob doesn't exist yet
|
|
|
|
|
FROM (SELECT NEW.noteId) AS note_select
|
|
|
|
|
LEFT JOIN blobs b ON b.blobId = NEW.blobId
|
|
|
|
|
WHERE NEW.isDeleted = 0
|
|
|
|
|
AND NEW.isProtected = 0;
|
2025-08-30 18:26:31 +00:00
|
|
|
END;
|
|
|
|
|
|
2025-09-02 19:24:46 +00:00
|
|
|
-- Trigger for UPDATE operations on blobs
|
|
|
|
|
-- Handles: Regular UPDATE and the UPDATE part of upsert (ON CONFLICT DO UPDATE)
|
|
|
|
|
-- IMPORTANT: Uses INSERT OR REPLACE for efficiency with deduplicated blobs
|
2025-08-30 18:26:31 +00:00
|
|
|
CREATE TRIGGER notes_fts_blob_update
|
|
|
|
|
AFTER UPDATE ON blobs
|
|
|
|
|
BEGIN
|
2025-09-02 19:24:50 +00:00
|
|
|
-- Use INSERT OR REPLACE for atomic update of all notes sharing this blob
|
|
|
|
|
-- This is more efficient than DELETE + INSERT when many notes share the same blob
|
2025-08-30 18:26:31 +00:00
|
|
|
INSERT OR REPLACE INTO notes_fts (noteId, title, content)
|
|
|
|
|
SELECT
|
|
|
|
|
n.noteId,
|
|
|
|
|
n.title,
|
|
|
|
|
NEW.content
|
|
|
|
|
FROM notes n
|
|
|
|
|
WHERE n.blobId = NEW.blobId
|
|
|
|
|
AND n.type IN ('text', 'code', 'mermaid', 'canvas', 'mindMap')
|
|
|
|
|
AND n.isDeleted = 0
|
|
|
|
|
AND n.isProtected = 0;
|
|
|
|
|
END;
|
|
|
|
|
|
2025-09-02 19:24:46 +00:00
|
|
|
-- Trigger for DELETE operations
|
2025-08-30 18:26:31 +00:00
|
|
|
CREATE TRIGGER notes_fts_delete
|
|
|
|
|
AFTER DELETE ON notes
|
|
|
|
|
BEGIN
|
|
|
|
|
DELETE FROM notes_fts WHERE noteId = OLD.noteId;
|
2025-09-02 19:24:46 +00:00
|
|
|
END;
|
|
|
|
|
|
|
|
|
|
-- Trigger for soft delete (isDeleted = 1)
|
|
|
|
|
CREATE TRIGGER notes_fts_soft_delete
|
|
|
|
|
AFTER UPDATE ON notes
|
|
|
|
|
WHEN OLD.isDeleted = 0 AND NEW.isDeleted = 1
|
|
|
|
|
BEGIN
|
|
|
|
|
DELETE FROM notes_fts WHERE noteId = NEW.noteId;
|
|
|
|
|
END;
|
|
|
|
|
|
|
|
|
|
-- Trigger for notes becoming protected
|
|
|
|
|
-- Remove from FTS when a note becomes protected
|
|
|
|
|
CREATE TRIGGER notes_fts_protect
|
|
|
|
|
AFTER UPDATE ON notes
|
|
|
|
|
WHEN OLD.isProtected = 0 AND NEW.isProtected = 1
|
|
|
|
|
BEGIN
|
|
|
|
|
DELETE FROM notes_fts WHERE noteId = NEW.noteId;
|
|
|
|
|
END;
|
|
|
|
|
|
|
|
|
|
-- Trigger for notes becoming unprotected
|
|
|
|
|
-- Add to FTS when a note becomes unprotected (if eligible)
|
|
|
|
|
CREATE TRIGGER notes_fts_unprotect
|
|
|
|
|
AFTER UPDATE ON notes
|
|
|
|
|
WHEN OLD.isProtected = 1 AND NEW.isProtected = 0
|
|
|
|
|
AND NEW.type IN ('text', 'code', 'mermaid', 'canvas', 'mindMap')
|
|
|
|
|
AND NEW.isDeleted = 0
|
|
|
|
|
BEGIN
|
|
|
|
|
DELETE FROM notes_fts WHERE noteId = NEW.noteId;
|
|
|
|
|
|
|
|
|
|
INSERT INTO notes_fts (noteId, title, content)
|
|
|
|
|
SELECT
|
|
|
|
|
NEW.noteId,
|
|
|
|
|
NEW.title,
|
|
|
|
|
COALESCE(b.content, '')
|
|
|
|
|
FROM (SELECT NEW.noteId) AS note_select
|
|
|
|
|
LEFT JOIN blobs b ON b.blobId = NEW.blobId;
|
|
|
|
|
END;
|
|
|
|
|
|
|
|
|
|
-- Trigger for INSERT operations on blobs
|
|
|
|
|
-- Handles: INSERT, INSERT OR REPLACE, and the INSERT part of upsert
|
|
|
|
|
-- Updates all notes that reference this blob (common during import and deduplication)
|
|
|
|
|
CREATE TRIGGER notes_fts_blob_insert
|
|
|
|
|
AFTER INSERT ON blobs
|
|
|
|
|
BEGIN
|
2025-09-02 19:24:50 +00:00
|
|
|
-- Use INSERT OR REPLACE to handle both new and existing FTS entries
|
|
|
|
|
-- This is crucial for blob deduplication where multiple notes may already
|
|
|
|
|
-- exist that reference this blob before the blob itself is created
|
2025-09-02 19:24:46 +00:00
|
|
|
INSERT OR REPLACE INTO notes_fts (noteId, title, content)
|
|
|
|
|
SELECT
|
|
|
|
|
n.noteId,
|
|
|
|
|
n.title,
|
|
|
|
|
NEW.content
|
|
|
|
|
FROM notes n
|
|
|
|
|
WHERE n.blobId = NEW.blobId
|
|
|
|
|
AND n.type IN ('text', 'code', 'mermaid', 'canvas', 'mindMap')
|
|
|
|
|
AND n.isDeleted = 0
|
|
|
|
|
AND n.isProtected = 0;
|
2025-08-30 18:26:31 +00:00
|
|
|
END;
|