wren/lost-property Public Public: anyone can read this; nobody can change it.

workbenchthis machine

Migrate the old note table instead of crashing on it

Production (and now the preview built from it) still had the note table
from the starter template: id, body, created_at, no item_id. CREATE TABLE
IF NOT EXISTS left that shape in place, so every query touching
note.item_id threw and / answered 500. A table missing item_id is now
renamed to note_legacy once, keeping its rows, before the current schema
is created.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Wren committed fe26fca Browse files
2 files changed +40 −0
src/db.test.ts added +30
@@ -0,0 +1,30 @@
1 import { expect, test } from "bun:test";
2 import { Database } from "bun:sqlite";
3 import { mkdtempSync } from "node:fs";
4 import { tmpdir } from "node:os";
5 import { join } from "node:path";
6
7 // Seed a data directory with the note table an earlier version of this app
8 // left behind: no item_id, so it does not fit the current schema.
9 const dir = mkdtempSync(join(tmpdir(), "app-"));
10 process.env.FORGE_APP_DATA = dir;
11 const seed = new Database(join(dir, "app.db"));
12 seed.exec(
13 "CREATE TABLE note (id INTEGER PRIMARY KEY, body TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (datetime('now')))",
14 );
15 seed.exec("INSERT INTO note (body) VALUES ('an old note from before items existed')");
16 seed.close();
17
18 const { db, createItem, addNote, listNotes, listItems } = await import("./db.ts");
19
20 test("moves an old, incompatible note table aside instead of crashing", () => {
21 const row = db.query<{ body: string }, []>("SELECT body FROM note_legacy").get();
22 expect(row?.body).toBe("an old note from before items existed");
23 });
24
25 test("the collection works normally after migrating", () => {
26 const id = createItem("a test item");
27 addNote(id, "sighting", "hello");
28 expect(listNotes(id)).toHaveLength(1);
29 expect(listItems()[0]?.note_count).toBe(1);
30 });
src/db.ts +10
@@ -13,6 +13,16 @@ const dataDir = process.env.FORGE_APP_DATA ?? "./data";
1313 mkdirSync(dataDir, { recursive: true });
1414 export const db = new Database(join(dataDir, "app.db"));
1515 db.exec("PRAGMA journal_mode = WAL");
16
17 // An earlier version of this app kept a flat note table with no item to
18 // belong to. `CREATE TABLE IF NOT EXISTS` below would leave that shape in
19 // place forever, so a table missing item_id is moved aside once, keeping
20 // its rows rather than losing them.
21 const existingNoteColumns = db.query<{ name: string }, []>("PRAGMA table_info(note)").all();
22 if (existingNoteColumns.length > 0 && !existingNoteColumns.some((c) => c.name === "item_id")) {
23 db.exec("ALTER TABLE note RENAME TO note_legacy");
24 }
25
1626 db.exec(
1727 `CREATE TABLE IF NOT EXISTS item (
1828 id INTEGER PRIMARY KEY,
↑↓ navigate ↵ open esc close

Keyboard shortcuts