db: rewrite migration with transactional schema versioning

This makes Stash's database handler a bit more robust. The changes
started as me trying to add an entry expiry, but I've realized that the
database system is a little fragile and it assumed the database does not
change, ever. Well that's not true, it does change and when it does
there's a chance that everything implodes.

We now wrap migrations in transaction for atomicity and track version
via PRAGMA user_version (0 -> 3). We also check column existence before
ALTER TABLE and use `last_insert_rowid()` instead of `next_sequence()`.

Last but not least, a bunch of regression tests have been added to the
database system because I'd rather not discover regressions in
production.

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Ifeab42b0816a5161d736767cb82065346a6a6964
This commit is contained in:
raf 2026-01-22 13:29:04 +03:00
commit c65073e0d1
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
2 changed files with 487 additions and 50 deletions

View file

@ -34,10 +34,11 @@ pub fn init_wayland_state() {
pub fn get_focused_window_app() -> Option<String> {
// Try Wayland protocol first
if let Ok(focused) = FOCUSED_APP.lock()
&& let Some(ref app) = *focused {
debug!("Found focused app via Wayland protocol: {app}");
return Some(app.clone());
}
&& let Some(ref app) = *focused
{
debug!("Found focused app via Wayland protocol: {app}");
return Some(app.clone());
}
debug!("No focused window detection method worked");
None
@ -80,10 +81,11 @@ impl Dispatch<wl_registry::WlRegistry, ()> for AppState {
interface,
version: _,
} = event
&& interface == "zwlr_foreign_toplevel_manager_v1" {
let _manager: ZwlrForeignToplevelManagerV1 =
registry.bind(name, 1, qh, ());
}
&& interface == "zwlr_foreign_toplevel_manager_v1"
{
let _manager: ZwlrForeignToplevelManagerV1 =
registry.bind(name, 1, qh, ());
}
}
fn event_created_child(
@ -152,10 +154,11 @@ impl Dispatch<ZwlrForeignToplevelHandleV1, ()> for AppState {
// Update focused app to the `app_id` of this handle
if let (Ok(apps), Ok(mut focused)) =
(TOPLEVEL_APPS.lock(), FOCUSED_APP.lock())
&& let Some(app_id) = apps.get(&handle_id) {
debug!("Setting focused app to: {app_id}");
*focused = Some(app_id.clone());
}
&& let Some(app_id) = apps.get(&handle_id)
{
debug!("Setting focused app to: {app_id}");
*focused = Some(app_id.clone());
}
}
},
zwlr_foreign_toplevel_handle_v1::Event::Closed => {