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:
kami
2026-07-30 23:36:11 +04:00
parent d497588c87
commit cd46ac397f
3 changed files with 29 additions and 11 deletions
+8 -3
View File
@@ -1147,9 +1147,14 @@ export class DbService {
async createArtist(data: Artist): Promise<Artist> {
const res = await this.pgClient.query(
`INSERT INTO artists (name, mbid, discogs_id, image_path)
VALUES (normalize_artist($1), $2, $3, $4) RETURNING *`,
[data.name, data.mbid, data.discogs_id, data.image_path]
// `canonical_name` is NOT NULL with no default, so omitting it fails on a
// fresh volume (the live DB's column predates the constraint). It holds the
// DISPLAY name: the raw input, not normalize_artist()'s output, which
// truncates on `/` and a standalone `x` ("AC/DC" -> "AC"). Matches the
// convention in workers' scanner.service.resolveOrCreateArtist.
`INSERT INTO artists (name, canonical_name, mbid, discogs_id, image_path)
VALUES (normalize_artist($1), $2, $3, $4, $5) RETURNING *`,
[data.name, data.name?.trim() || data.name, data.mbid, data.discogs_id, data.image_path]
);
return res.rows[0];
}