Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/backend/database/db/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { repairSnippetsColumns } from "../../upgrade/snippets-schema-migration.js";
import { repairLegacyPluginColumns } from "../../upgrade/legacy-plugin-columns.js";
import { getErrorMessage } from "../../utils/error-message.js";
import { drizzle } from "drizzle-orm/better-sqlite3";
import Database from "better-sqlite3";
Expand Down Expand Up @@ -1024,7 +1024,7 @@ const enforceUniqueUsernames = () => {
};

const migrateSchema = () => {
repairSnippetsColumns(sqlite);
repairLegacyPluginColumns(sqlite);
addColumnIfNotExists("api_keys", "scopes", "TEXT");
addColumnIfNotExists("user_preferences", "theme", "TEXT");
addColumnIfNotExists("user_preferences", "font_size", "TEXT");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Database from "better-sqlite3";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { repairSnippetsColumns } from "../../upgrade/snippets-schema-migration.js";
import { repairLegacyPluginColumns } from "../../upgrade/legacy-plugin-columns.js";

let sqlite: Database.Database;
beforeEach(() => {
Expand All @@ -23,7 +23,7 @@ describe("snippet column upgrade", () => {
expect(() => sqlite.prepare(`SELECT "is_note" FROM "${table}"`)).toThrow(
/is_note/,
);
repairSnippetsColumns(sqlite);
repairLegacyPluginColumns(sqlite);
if (table === "snippets") {
// What the plugin migration does to the legacy table.
sqlite.exec(`ALTER TABLE "snippets" RENAME TO "p_snippets_snippets"`);
Expand All @@ -34,15 +34,15 @@ describe("snippet column upgrade", () => {
.get(),
).toEqual({ id: 7, content: "echo hello", is_note: 0 });
sqlite.exec("UPDATE p_snippets_snippets SET is_note = 1");
repairSnippetsColumns(sqlite);
repairLegacyPluginColumns(sqlite);
expect(
sqlite.prepare("SELECT is_note FROM p_snippets_snippets").get(),
).toEqual({ is_note: 1 });
},
);

it("does not create tables on a fresh install", () => {
repairSnippetsColumns(sqlite);
repairLegacyPluginColumns(sqlite);
expect(
sqlite
.prepare("SELECT name FROM sqlite_master WHERE type = 'table'")
Expand All @@ -61,7 +61,7 @@ describe("snippet column upgrade", () => {
`INSERT INTO "p_snippets_snippets" VALUES (1, 'u', 'a', 'ls'), (2, 'u', 'b', 'pwd')`,
);
sqlite.exec(`INSERT INTO "snippet_folders" VALUES (1, 'u', 'ops')`);
repairSnippetsColumns(sqlite);
repairLegacyPluginColumns(sqlite);

const snippets = sqlite
.prepare(
Expand All @@ -84,11 +84,42 @@ describe("snippet column upgrade", () => {
const before = sqlite
.prepare(`SELECT "sync_id" FROM "p_snippets_snippets" ORDER BY id`)
.all();
repairSnippetsColumns(sqlite);
repairLegacyPluginColumns(sqlite);
expect(
sqlite
.prepare(`SELECT "sync_id" FROM "p_snippets_snippets" ORDER BY id`)
.all(),
).toEqual(before);
});

it("adds the 2.8 columns to homepage and vault tables adopted from 2.5", () => {
// 2.5.1's columns, renamed by the plugins' adoption migrations.
sqlite.exec(`
CREATE TABLE "p_homepage_homepage_items" (id INTEGER PRIMARY KEY, user_id TEXT, type_id TEXT, title TEXT, config TEXT, folder_id INTEGER, created_at TEXT, updated_at TEXT);
CREATE TABLE "p_homepage_dashboard_service_links" (id INTEGER PRIMARY KEY, user_id TEXT, label TEXT, url TEXT, "order" INTEGER, created_at TEXT);
CREATE TABLE "p_vault_profiles" (id INTEGER PRIMARY KEY, user_id TEXT, name TEXT, vault_addr TEXT, ssh_role TEXT);
INSERT INTO "p_homepage_homepage_items" (id, user_id, type_id) VALUES (1, 'u', 'clock');
INSERT INTO "p_homepage_dashboard_service_links" (id, user_id, label, url) VALUES (1, 'u', 'Wiki', 'https://wiki');
INSERT INTO "p_vault_profiles" (id, user_id, name) VALUES (1, 'u', 'prod');
`);
repairLegacyPluginColumns(sqlite);

for (const table of [
"p_homepage_homepage_items",
"p_homepage_dashboard_service_links",
"p_vault_profiles",
]) {
const row = sqlite.prepare(`SELECT "sync_id" FROM "${table}"`).get() as {
sync_id: string;
};
expect(row.sync_id).toMatch(/^[0-9a-f]{32}$/);
}
expect(
sqlite
.prepare(
`SELECT "label", "updated_at" FROM "p_homepage_dashboard_service_links"`,
)
.get(),
).toEqual({ label: "Wiki", updated_at: null });
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,32 @@ const FOLDER_COLUMNS: Array<[string, string]> = [
["sync_id", "TEXT"],
];

// 2.8 added these to core's tables on start. 2.9 left the tables to plugins
// and dropped that code, so a database that skipped 2.8 never got them.
const SYNC_ID: Array<[string, string]> = [["sync_id", "TEXT"]];
const SERVICE_LINK_COLUMNS: Array<[string, string]> = [
["sync_id", "TEXT"],
["updated_at", "TEXT"],
];

const TABLES: Array<[string, Array<[string, string]>]> = [
["snippets", SNIPPET_COLUMNS],
["p_snippets_snippets", SNIPPET_COLUMNS],
["snippet_folders", FOLDER_COLUMNS],
["p_snippets_snippet_folders", FOLDER_COLUMNS],
["homepage_items", SYNC_ID],
["p_homepage_homepage_items", SYNC_ID],
["dashboard_service_links", SERVICE_LINK_COLUMNS],
["p_homepage_dashboard_service_links", SERVICE_LINK_COLUMNS],
["vault_profiles", SYNC_ID],
["p_vault_profiles", SYNC_ID],
];

/** Repair older SQLite tables before adoption, or after an incomplete 2.9 upgrade. */
export function repairSnippetsColumns(sqlite: Database.Database): void {
/**
* Repairs older SQLite tables plugins adopt, before adoption or after an
* upgrade that skipped the release adding their columns.
*/
export function repairLegacyPluginColumns(sqlite: Database.Database): void {
for (const [table, wanted] of TABLES) {
const columns = sqlite.pragma(`table_info("${table}")`) as Array<{
name: string;
Expand Down
Loading