From be200641cd489b409bcd1ff4ecb8edf11e8c2692 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Tue, 6 Oct 2026 19:42:51 -0400 Subject: [PATCH] core: notes show their real created and edited times, and desktop search works MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both bugs were caught red by the tests in 732fd7a (run 8418: 5 failed, 147 passed) before this fix. - load_note reads columns by NAME. Dropping `color` (fa89da1) shifted every column after it and the two timestamps were missed, so created_at showed the last edit and updated_at showed the trash time — null on any live note. Only the read was wrong; nothing stored is, so no data needs repairing. - The board's text facet binds its pattern once for its one placeholder. It pushed it twice after the title column went (95aa10c), and rusqlite refused every query with InvalidParameterCount — every desktop search failed. Android searches through store::search and was never affected. Co-Authored-By: Claude Opus 5.5 --- core/src/local/store.rs | 43 ++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/core/src/local/store.rs b/core/src/local/store.rs index e0923a3..075db4d 100644 --- a/core/src/local/store.rs +++ b/core/src/local/store.rs @@ -144,25 +144,28 @@ fn load_note(conn: &Connection, id: &str) -> rusqlite::Result { "SELECT id, body, position, pinned, archived, trashed, remind_at, recurrence, created_at, updated_at, trashed_at FROM notes WHERE id = ?1", [id], + // By NAME, not by position. Dropping `color` shifted every column after it + // and the two timestamps were missed (fa89da1): created_at read updated_at + // and updated_at read trashed_at, with nothing failing. A name either + // resolves or errors. |r| { - let body: String = r.get(1)?; Ok(Note { - id: r.get(0)?, - display_title: String::new(), // filled below — it may need a query - body, - position: r.get(2)?, - pinned: r.get(3)?, - archived: r.get(4)?, - trashed: r.get(5)?, - deleted_at: r.get(10)?, - remind_at: r.get(6)?, - recurrence: r.get(7)?, + id: r.get("id")?, + display_title: String::new(), // derived below, from the body + body: r.get("body")?, + position: r.get("position")?, + pinned: r.get("pinned")?, + archived: r.get("archived")?, + trashed: r.get("trashed")?, + deleted_at: r.get("trashed_at")?, + remind_at: r.get("remind_at")?, + recurrence: r.get("recurrence")?, labels: Vec::new(), items: Vec::new(), attachments: Vec::new(), previews: Vec::new(), - created_at: r.get(9)?, - updated_at: r.get(10)?, + created_at: r.get("created_at")?, + updated_at: r.get("updated_at")?, }) }, )?; @@ -321,10 +324,11 @@ pub fn list_notes(conn: &Connection, q: &ListQuery) -> rusqlite::Result rusqlite::Result> { } pub fn titles(conn: &Connection) -> rusqlite::Result> { - // Names come from `load_note` rather than from a bare row, because a note whose - // body is empty is named by its first checklist item — which a row here doesn't - // have. The command palette reads this; correctness beats one query per note at - // personal scale. + // Names come from `load_note` so the palette and the card can never disagree + // about what a note is called. The command palette reads this; correctness + // beats one query per note at personal scale. let ids: Vec = { let mut stmt = conn.prepare("SELECT id FROM notes WHERE trashed = 0")?; let rows = stmt.query_map([], |r| r.get(0))?;