diff --git a/src/vault/euph.rs b/src/vault/euph.rs index 1e9df1b..2517ba9 100644 --- a/src/vault/euph.rs +++ b/src/vault/euph.rs @@ -1224,10 +1224,9 @@ impl EuphRequest { let amount = conn .prepare( " - SELECT COUNT(*) - FROM euph_msgs + SELECT amount + FROM euph_unseen_counts WHERE room = ? - AND NOT seen ", )? .query_row(params![room], |row| row.get(0))?; diff --git a/src/vault/prepare.rs b/src/vault/prepare.rs index 8f55fb0..8a9e67b 100644 --- a/src/vault/prepare.rs +++ b/src/vault/prepare.rs @@ -2,6 +2,7 @@ use rusqlite::Connection; pub fn prepare(conn: &mut Connection) -> rusqlite::Result<()> { println!("Opening vault"); + // This temporary table has no foreign key constraint on euph_rooms since // cross-schema constraints like that are not supported by SQLite. conn.execute_batch( @@ -28,5 +29,73 @@ pub fn prepare(conn: &mut Connection) -> rusqlite::Result<()> { AND parents.id = euph_msgs.parent ); ", - ) + )?; + + // Cache amount of unseen messages per room because counting them takes far + // too long. Uses triggers to move as much of the updating logic as possible + // into SQLite. + conn.execute_batch( + " + CREATE TEMPORARY TABLE euph_unseen_counts ( + room TEXT NOT NULL, + amount INTEGER NOT NULL, + + PRIMARY KEY (room) + ) STRICT; + + -- There must be an entry for every existing room. + INSERT INTO euph_unseen_counts (room, amount) + SELECT room, 0 + FROM euph_rooms; + + INSERT OR REPLACE INTO euph_unseen_counts (room, amount) + SELECT room, COUNT(*) + FROM euph_msgs + WHERE NOT seen + GROUP BY room; + + CREATE TEMPORARY TRIGGER euc_insert_room + AFTER INSERT ON euph_rooms + BEGIN + INSERT INTO euph_unseen_counts (room, amount) + VALUES (new.room, 0); + END; + + CREATE TEMPORARY TRIGGER euc_delete_room + AFTER DELETE ON euph_rooms + BEGIN + DELETE FROM euph_unseen_counts + WHERE room = old.room; + END; + + CREATE TEMPORARY TRIGGER euc_insert_msg + AFTER INSERT ON euph_msgs + WHEN NOT new.seen + BEGIN + UPDATE euph_unseen_counts + SET amount = amount + 1 + WHERE room = new.room; + END; + + CREATE TEMPORARY TRIGGER euc_update_msg + AFTER UPDATE OF seen ON euph_msgs + WHEN old.seen != new.seen + BEGIN + UPDATE euph_unseen_counts + SET amount = CASE WHEN new.seen THEN amount - 1 ELSE amount + 1 END + WHERE room = new.room; + END; + + CREATE TEMPORARY TRIGGER euc_delete_msg + AFTER DELETE ON euph_msgs + WHEN NOT old.seen + BEGIN + UPDATE euph_unseen_counts + SET amount = amount - 1 + WHERE room = old.room; + END; + ", + )?; + + Ok(()) }