fix(media-items): normalize NOT NULL narrators on every write path
A2 made narrators/narrators_search NOT NULL DEFAULT '{}' (86f0af6), but an
explicit NULL in an INSERT/UPDATE column list bypasses the column default
— the API media-create path (POST /api/media-items without narrators)
500'd on every call, taking the whole integration suite down with it
(ISBN tests, capture seeds, all direct-DB seeds).
- set_library_type_name() trigger now coalesces NULL narrators/
narrators_search to '{}' at the row level, and its update trigger
widens from BEFORE UPDATE OF library_id to BEFORE UPDATE so every
write path is covered
- defense in depth at the callers: handler create/update normalize nil
to empty slices (normalizeNarrators), scanner seeds the same
(nilToEmpty) for files that carry no narrator credits
- analytics device-usage tests move to the dedicated per-test user
(the shared admin's device list accrues live rows mid-suite — the
documented testuser flapping class)
This commit is contained in:
1 parent
73ae1789c5
commit
a222ef5c27
5 files changed
+56
-13
No files matched your search
@@ -169,6 +169,17 @@ type CalibreOPFMetadata struct {
|
||||
// watcher (and thus can never panic on EMFILE/ENOSPC). This fixes the
|
||||
// fd/inotify-watch leak where every scan job created a watcher that was
|
||||
// never closed.
|
||||
// nilToEmpty guards the NOT NULL narrators columns (A2): a Go nil slice
|
||||
// becomes an explicit NULL in INSERT/UPDATE params and 23502s — the
|
||||
// column DEFAULT never fires for explicit NULLs. "No narrators known"
|
||||
// is an empty array, never NULL.
|
||||
func nilToEmpty(slice []string) []string {
|
||||
if slice == nil {
|
||||
return []string{}
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
func NewMediaScanner(db *database.Queries) *MediaScanner {
|
||||
return &MediaScanner{
|
||||
db: db,
|
||||
@@ -947,7 +958,7 @@ func (s *MediaScanner) processMediaFile(ctx context.Context, path string) (bool,
|
||||
DatePublished: pgtype.Date{Time: metadata.PublishDate, Valid: !metadata.PublishDate.IsZero()},
|
||||
Contributors: metadata.Contributors,
|
||||
ContributorsSearch: contributorsSearch,
|
||||
Narrators: metadata.Narrators,
|
||||
Narrators: nilToEmpty(metadata.Narrators),
|
||||
NarratorsSearch: utils.NormalizeNarratorsSearch(metadata.Narrators),
|
||||
Abridged: metadata.Abridged,
|
||||
Tags: metadata.Tags,
|
||||
@@ -3210,7 +3221,7 @@ func (s *MediaScanner) updateMediaItem(ctx context.Context, existing database.Me
|
||||
CoverImagePath: pgtype.Text{String: s.getRelativePath(metadata.CoverPath), Valid: metadata.CoverPath != ""},
|
||||
Series: pgtype.Text{String: metadata.Series, Valid: metadata.Series != ""},
|
||||
SeriesNumber: pgtype.Int4{Int32: metadata.SeriesNumber, Valid: metadata.SeriesNumber > 0},
|
||||
Narrators: metadata.Narrators,
|
||||
Narrators: nilToEmpty(metadata.Narrators),
|
||||
NarratorsSearch: utils.NormalizeNarratorsSearch(metadata.Narrators),
|
||||
Abridged: metadata.Abridged,
|
||||
Tags: metadata.Tags,
|
||||
|
||||
Reference in new issue
Block a user