fix: populate canonical_name and stop writing generated normalized_name
Three separate insert paths made a fresh Postgres volume unusable. The live
database only works because its volume predates the constraints.
- scanner.service.resolveOrCreateArtist inserted only (name), but
schema.sql declares canonical_name NOT NULL with no default. Every
artist insert failed, and processFile swallows per-file errors, so a
scan reported success with 0 tracks and a permanently empty library.
- enrichment.service inserted explicitly into artists.normalized_name,
which is GENERATED ALWAYS AS (normalize_artist(name)) STORED:
"cannot insert a non-DEFAULT value into column" (428C9). All
enrichment artist creation failed on a fresh volume.
- db.service.createArtist omitted canonical_name, same failure.
canonical_name holds the raw tag name, not normalize_artist() output,
which truncates on `/` and a standalone `x` ("AC/DC" -> "AC"). That is the
convention createLocalArtist already used. The truncation bug in
artists.name is pre-existing and deliberately left untouched here.
Verified on a scratch postgres:16-alpine with the real schema: the old
statement reproduces the NOT NULL violation, the new path yields
artists/albums/tracks/track_artists rows.
REVIEW-2026-07-30.md finding 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -237,9 +237,17 @@ export class ScannerService {
|
||||
return { id: String(found.rows[0].id), name: String(found.rows[0].name) };
|
||||
}
|
||||
|
||||
// `canonical_name` is NOT NULL in schema.sql with no default, so it MUST be
|
||||
// supplied here — omitting it makes every artist insert fail on a fresh
|
||||
// volume (and processFile swallows the error, so the scan silently yields an
|
||||
// empty library). It holds the DISPLAY name: we store the raw tag name, not
|
||||
// the normalize_artist() output, because that function truncates on `/` and
|
||||
// a standalone `x` ("AC/DC" -> "AC", "Felix Mendelssohn" -> "Feli"). The
|
||||
// enrichment path later overwrites canonical_name with the MusicBrainz name;
|
||||
// until then the raw tag is the most faithful display value we have.
|
||||
const inserted = await this.pgClient.query(
|
||||
'INSERT INTO artists (name) VALUES ($1) RETURNING id, name',
|
||||
[name]
|
||||
'INSERT INTO artists (name, canonical_name) VALUES ($1, $2) RETURNING id, name',
|
||||
[name, rawName.trim() || name]
|
||||
);
|
||||
return { id: String(inserted.rows[0].id), name: String(inserted.rows[0].name) };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user