sync: fail loudly on upsert column/value drift

Review follow-ups for the replay gain slice.

`upsertChanged` never checked that its hand-maintained column list and
the row values line up, and SQLite binds a missing trailing value as NULL
without error: adding a column to `songColumns` but not to `Song.values()`
would have written NULLs silently. It now asserts the sizes match before
preparing, so that mistake fails the sync instead.

The replay gain sync test also now re-syncs identical values and asserts
no rows are rewritten, pinning the REAL change-detection clause; an
update there rewrites the `song_search` FTS row for no reason.
This commit is contained in:
austinried
2026-10-16 13:06:20 +00:00
parent 526373c10a
commit 86d0d06e76
2 changed files with 5 additions and 0 deletions
@@ -382,6 +382,7 @@ private suspend fun <T> PooledConnection.upsertChanged(
values: (T) -> List<Any?>,
) {
if (rows.isEmpty()) return
check(values(rows.first()).size == columns.size) { "column/value mismatch for $table" }
val updates = columns.filterNot { it in key }
val sql =
"INSERT INTO $table (${columns.joinToString()}) VALUES (${columns.joinToString { "?" }}) " +
@@ -913,6 +913,10 @@ class SyncServiceTest {
assertEquals(0.98f, stored.trackPeak)
assertEquals(0.99f, stored.albumPeak)
val before = totalChanges()
SyncService(db, source).sync()
assertEquals(before, totalChanges())
source.songs = listOf(song("s1"))
SyncService(db, source).sync()