fix: use sql.NullTime for last_played to handle NULL scan

COALESCE(last_played, '') returned empty string which can't scan into
time.Time. Removed COALESCE, use sql.NullTime instead. Format to string
only when Valid.
This commit is contained in:
2026-03-21 15:40:34 -04:00
parent b643aee5a3
commit 2db6e09aa3
3 changed files with 33 additions and 21 deletions
+2 -2
View File
@@ -97,7 +97,7 @@ SELECT
af.bitrate, af.bitrate,
af.file_size, af.file_size,
af.play_count, af.play_count,
COALESCE(af.last_played, '') AS last_played af.last_played
FROM audio_files af FROM audio_files af
JOIN recordings r ON af.recording_id = r.id JOIN recordings r ON af.recording_id = r.id
JOIN artist_credit ac ON r.artist_credit_id = ac.id JOIN artist_credit ac ON r.artist_credit_id = ac.id
@@ -163,7 +163,7 @@ SELECT
af.bitrate, af.bitrate,
af.file_size, af.file_size,
af.play_count, af.play_count,
COALESCE(af.last_played, '') AS last_played af.last_played
FROM audio_files af FROM audio_files af
JOIN recordings r ON af.recording_id = r.id JOIN recordings r ON af.recording_id = r.id
JOIN artist_credit ac ON r.artist_credit_id = ac.id JOIN artist_credit ac ON r.artist_credit_id = ac.id
+22 -10
View File
@@ -35,7 +35,7 @@ func (q *Queries) CountAudioFilesByLibrary(ctx context.Context, libraryID int64)
const createAudioFile = `-- name: CreateAudioFile :one const createAudioFile = `-- name: CreateAudioFile :one
INSERT INTO audio_files (file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) INSERT INTO audio_files (file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
RETURNING id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id RETURNING id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id, play_count, last_played
` `
type CreateAudioFileParams struct { type CreateAudioFileParams struct {
@@ -80,6 +80,8 @@ func (q *Queries) CreateAudioFile(ctx context.Context, arg CreateAudioFileParams
&i.FileSize, &i.FileSize,
&i.Basename, &i.Basename,
&i.LibraryID, &i.LibraryID,
&i.PlayCount,
&i.LastPlayed,
) )
return i, err return i, err
} }
@@ -136,7 +138,7 @@ func (q *Queries) GetAllAudioFilePaths(ctx context.Context) ([]GetAllAudioFilePa
} }
const getAllAudioFiles = `-- name: GetAllAudioFiles :many const getAllAudioFiles = `-- name: GetAllAudioFiles :many
SELECT id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id FROM audio_files SELECT id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id, play_count, last_played FROM audio_files
` `
func (q *Queries) GetAllAudioFiles(ctx context.Context) ([]AudioFile, error) { func (q *Queries) GetAllAudioFiles(ctx context.Context) ([]AudioFile, error) {
@@ -161,6 +163,8 @@ func (q *Queries) GetAllAudioFiles(ctx context.Context) ([]AudioFile, error) {
&i.FileSize, &i.FileSize,
&i.Basename, &i.Basename,
&i.LibraryID, &i.LibraryID,
&i.PlayCount,
&i.LastPlayed,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@@ -255,7 +259,7 @@ SELECT
af.bitrate, af.bitrate,
af.file_size, af.file_size,
af.play_count, af.play_count,
COALESCE(af.last_played, '') AS last_played af.last_played
FROM audio_files af FROM audio_files af
JOIN recordings r ON af.recording_id = r.id JOIN recordings r ON af.recording_id = r.id
JOIN artist_credit ac ON r.artist_credit_id = ac.id JOIN artist_credit ac ON r.artist_credit_id = ac.id
@@ -282,7 +286,7 @@ type GetAllTracksWithFullMetadataRow struct {
Bitrate int64 Bitrate int64
FileSize int64 FileSize int64
PlayCount int64 PlayCount int64
LastPlayed string LastPlayed sql.NullTime
} }
func (q *Queries) GetAllTracksWithFullMetadata(ctx context.Context) ([]GetAllTracksWithFullMetadataRow, error) { func (q *Queries) GetAllTracksWithFullMetadata(ctx context.Context) ([]GetAllTracksWithFullMetadataRow, error) {
@@ -352,7 +356,7 @@ SELECT
af.bitrate, af.bitrate,
af.file_size, af.file_size,
af.play_count, af.play_count,
COALESCE(af.last_played, '') AS last_played af.last_played
FROM audio_files af FROM audio_files af
JOIN recordings r ON af.recording_id = r.id JOIN recordings r ON af.recording_id = r.id
JOIN artist_credit ac ON r.artist_credit_id = ac.id JOIN artist_credit ac ON r.artist_credit_id = ac.id
@@ -380,7 +384,7 @@ type GetAllTracksWithFullMetadataByLibraryRow struct {
Bitrate int64 Bitrate int64
FileSize int64 FileSize int64
PlayCount int64 PlayCount int64
LastPlayed string LastPlayed sql.NullTime
} }
func (q *Queries) GetAllTracksWithFullMetadataByLibrary(ctx context.Context, libraryID int64) ([]GetAllTracksWithFullMetadataByLibraryRow, error) { func (q *Queries) GetAllTracksWithFullMetadataByLibrary(ctx context.Context, libraryID int64) ([]GetAllTracksWithFullMetadataByLibraryRow, error) {
@@ -426,7 +430,7 @@ func (q *Queries) GetAllTracksWithFullMetadataByLibrary(ctx context.Context, lib
} }
const getAudioFile = `-- name: GetAudioFile :one const getAudioFile = `-- name: GetAudioFile :one
SELECT id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id FROM audio_files SELECT id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id, play_count, last_played FROM audio_files
WHERE id = ? LIMIT 1 WHERE id = ? LIMIT 1
` `
@@ -446,12 +450,14 @@ func (q *Queries) GetAudioFile(ctx context.Context, id int64) (AudioFile, error)
&i.FileSize, &i.FileSize,
&i.Basename, &i.Basename,
&i.LibraryID, &i.LibraryID,
&i.PlayCount,
&i.LastPlayed,
) )
return i, err return i, err
} }
const getAudioFileByPath = `-- name: GetAudioFileByPath :one const getAudioFileByPath = `-- name: GetAudioFileByPath :one
SELECT id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id FROM audio_files SELECT id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id, play_count, last_played FROM audio_files
WHERE file_path = ? LIMIT 1 WHERE file_path = ? LIMIT 1
` `
@@ -471,12 +477,14 @@ func (q *Queries) GetAudioFileByPath(ctx context.Context, filePath string) (Audi
&i.FileSize, &i.FileSize,
&i.Basename, &i.Basename,
&i.LibraryID, &i.LibraryID,
&i.PlayCount,
&i.LastPlayed,
) )
return i, err return i, err
} }
const getAudioFilesByLibrary = `-- name: GetAudioFilesByLibrary :many const getAudioFilesByLibrary = `-- name: GetAudioFilesByLibrary :many
SELECT id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id FROM audio_files WHERE library_id = ? SELECT id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id, play_count, last_played FROM audio_files WHERE library_id = ?
` `
func (q *Queries) GetAudioFilesByLibrary(ctx context.Context, libraryID int64) ([]AudioFile, error) { func (q *Queries) GetAudioFilesByLibrary(ctx context.Context, libraryID int64) ([]AudioFile, error) {
@@ -501,6 +509,8 @@ func (q *Queries) GetAudioFilesByLibrary(ctx context.Context, libraryID int64) (
&i.FileSize, &i.FileSize,
&i.Basename, &i.Basename,
&i.LibraryID, &i.LibraryID,
&i.PlayCount,
&i.LastPlayed,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@@ -707,7 +717,7 @@ func (q *Queries) GetAudioFilesByReleaseGroupByLibrary(ctx context.Context, arg
} }
const getAudioFilesNeedingMetadata = `-- name: GetAudioFilesNeedingMetadata :many const getAudioFilesNeedingMetadata = `-- name: GetAudioFilesNeedingMetadata :many
SELECT id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id FROM audio_files SELECT id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id, play_count, last_played FROM audio_files
WHERE recording_id = 0 WHERE recording_id = 0
` `
@@ -733,6 +743,8 @@ func (q *Queries) GetAudioFilesNeedingMetadata(ctx context.Context) ([]AudioFile
&i.FileSize, &i.FileSize,
&i.Basename, &i.Basename,
&i.LibraryID, &i.LibraryID,
&i.PlayCount,
&i.LastPlayed,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
+9 -9
View File
@@ -67,11 +67,11 @@ func mapTrackRow(
composer, fileType string, composer, fileType string,
sampleRate, bitDepth, channels, bitrate, fileSize int64, sampleRate, bitDepth, channels, bitrate, fileSize int64,
playCount int64, playCount int64,
lastPlayed time.Time, lastPlayed sql.NullTime,
) Track { ) Track {
var lastPlayedStr string var lastPlayedStr string
if !lastPlayed.IsZero() { if lastPlayed.Valid {
lastPlayedStr = lastPlayed.Format(time.DateTime) lastPlayedStr = lastPlayed.Time.Format(time.DateTime)
} }
return Track{ return Track{
@@ -210,7 +210,7 @@ func (l *Library) SearchTracks(
row.Channels, row.Channels,
row.Bitrate, row.Bitrate,
row.FileSize, row.FileSize,
0, time.Time{}, 0, sql.NullTime{},
)) ))
} }
@@ -250,7 +250,7 @@ func (l *Library) GetAlbumTracks(albumID int64) ([]Track, error) {
row.Channels, row.Channels,
row.Bitrate, row.Bitrate,
row.FileSize, row.FileSize,
0, time.Time{}, 0, sql.NullTime{},
)) ))
} }
@@ -425,7 +425,7 @@ func (l *Library) GetTracksByGenre(
row.Channels, row.Channels,
row.Bitrate, row.Bitrate,
row.FileSize, row.FileSize,
0, time.Time{}, 0, sql.NullTime{},
)) ))
} }
@@ -741,7 +741,7 @@ func (l *Library) GetTracksByGenreByLibrary(
row.Channels, row.Channels,
row.Bitrate, row.Bitrate,
row.FileSize, row.FileSize,
0, time.Time{}, 0, sql.NullTime{},
)) ))
} }
@@ -793,7 +793,7 @@ func (l *Library) GetAlbumTracksByLibrary(
row.Channels, row.Channels,
row.Bitrate, row.Bitrate,
row.FileSize, row.FileSize,
0, time.Time{}, 0, sql.NullTime{},
)) ))
} }
@@ -841,7 +841,7 @@ func (l *Library) SearchTracksByLibrary(
row.Channels, row.Channels,
row.Bitrate, row.Bitrate,
row.FileSize, row.FileSize,
0, time.Time{}, 0, sql.NullTime{},
)) ))
} }