From 2c88cb8ffe69609b67b69836923f8df7e4ac453f Mon Sep 17 00:00:00 2001 From: Sam Chau Date: Mon, 19 Jan 2026 04:39:31 +1030 Subject: [PATCH] fix: mass FK lobotomy - turns out IDs shift when you replay ops, also adding notify workflow to schema repo (hello reader) --- .github/workflows/notify.yml | 10 ++ CHANGELOG.md | 33 +++++- ops/0.schema.sql | 207 +++++++++++++++++++++-------------- 3 files changed, 164 insertions(+), 86 deletions(-) create mode 100644 .github/workflows/notify.yml diff --git a/.github/workflows/notify.yml b/.github/workflows/notify.yml new file mode 100644 index 0000000..de4daec --- /dev/null +++ b/.github/workflows/notify.yml @@ -0,0 +1,10 @@ +name: Notify +on: + push: + branches: + - "**" +jobs: + call-notify-commit: + uses: Dictionarry-Hub/parrot/.github/workflows/notify-commit.yml@v1 + secrets: + PARROT_URL: ${{ secrets.PARROT_URL }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 325533e..3ed1e3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,7 +57,32 @@ All schema changes will be documented in this file. - Supports languages, indexers, and flags as JSON arrays for release metadata ## 19-1-26 -- Fix test_releases FK stability issue - - Changed from test_entity_id (auto-generated) to composite FK (entity_type, entity_tmdb_id) - - Ensures releases remain correctly linked after database recompile from ops - - Updated index to use composite key \ No newline at end of file + +### Major FK Stability Overhaul +All tables now use stable name-based keys instead of autoincrement IDs for foreign key +references. This ensures data remains correctly linked after database recompile from ops. + +**Core principle:** Every FK now references a UNIQUE name column (or composite of names) +instead of an autoincrement id column. + +- **test_releases**: Changed from test_entity_id to composite FK (entity_type, entity_tmdb_id) +- **custom_format_conditions**: Changed custom_format_id to custom_format_name + - Added UNIQUE(custom_format_name, name) - condition names must be unique within a CF +- **All condition child tables** (condition_patterns, condition_languages, condition_indexer_flags, + condition_sources, condition_resolutions, condition_quality_modifiers, condition_sizes, + condition_release_types, condition_years): + - Use (custom_format_name, condition_name) composite FK + - condition_patterns also uses regular_expression_name + - condition_languages also uses language_name +- **quality_groups**: Changed quality_profile_id to quality_profile_name +- **quality_group_members**: Uses (quality_profile_name, quality_group_name, quality_name) +- **quality_profile_qualities**: Uses quality_profile_name, quality_name, quality_group_name +- **quality_profile_custom_formats**: Uses (quality_profile_name, custom_format_name) +- **quality_profile_languages**: Uses (quality_profile_name, language_name) +- **quality_profile_tags**: Uses (quality_profile_name, tag_name) +- **regular_expression_tags**: Uses (regular_expression_name, tag_name) +- **custom_format_tags**: Uses (custom_format_name, tag_name) +- **delay_profile_tags**: Uses (delay_profile_name, tag_name) +- **quality_api_mappings**: Uses (quality_name, arr_type) +- **custom_format_tests**: Uses custom_format_name +- **radarr_quality_definitions / sonarr_quality_definitions**: Uses quality_name as PK \ No newline at end of file diff --git a/ops/0.schema.sql b/ops/0.schema.sql index e44fce2..8826793 100644 --- a/ops/0.schema.sql +++ b/ops/0.schema.sql @@ -43,13 +43,14 @@ CREATE TABLE qualities ( -- Maps Profilarr canonical qualities to arr-specific API names -- Absence of a row means the quality doesn't exist for that arr +-- Uses stable key: quality_name CREATE TABLE quality_api_mappings ( - quality_id INTEGER NOT NULL, + quality_name VARCHAR(100) NOT NULL, arr_type VARCHAR(20) NOT NULL, -- 'radarr', 'sonarr' api_name VARCHAR(100) NOT NULL, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (quality_id, arr_type), - FOREIGN KEY (quality_id) REFERENCES qualities(id) ON DELETE CASCADE + PRIMARY KEY (quality_name, arr_type), + FOREIGN KEY (quality_name) REFERENCES qualities(name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Custom formats define patterns and conditions for media matching @@ -81,29 +82,32 @@ CREATE TABLE quality_profiles ( -- Quality groups combine multiple qualities treated as equivalent -- Each group is specific to a quality profile (profiles do not share groups) +-- Uses stable key: quality_profile_name instead of quality_profile_id CREATE TABLE quality_groups ( id INTEGER PRIMARY KEY AUTOINCREMENT, - quality_profile_id INTEGER NOT NULL, + quality_profile_name VARCHAR(100) NOT NULL, name VARCHAR(100) NOT NULL, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE(quality_profile_id, name), - FOREIGN KEY (quality_profile_id) REFERENCES quality_profiles(id) ON DELETE CASCADE + UNIQUE(quality_profile_name, name), + FOREIGN KEY (quality_profile_name) REFERENCES quality_profiles(name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Conditions define the matching logic for custom formats -- Each condition has a type and corresponding data in a type-specific table +-- Uses custom_format_name (stable) instead of custom_format_id (autoincrement) CREATE TABLE custom_format_conditions ( id INTEGER PRIMARY KEY AUTOINCREMENT, - custom_format_id INTEGER NOT NULL, + custom_format_name VARCHAR(100) NOT NULL, name VARCHAR(100) NOT NULL, type VARCHAR(50) NOT NULL, - arr_type VARCHAR(20) NOT NULL, -- 'radarr', 'sonarr', 'all' + arr_type VARCHAR(20) NOT NULL DEFAULT 'all', -- 'radarr', 'sonarr', 'all' negate INTEGER NOT NULL DEFAULT 0, required INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (custom_format_id) REFERENCES custom_formats(id) ON DELETE CASCADE + UNIQUE(custom_format_name, name), + FOREIGN KEY (custom_format_name) REFERENCES custom_formats(name) ON DELETE CASCADE ON UPDATE CASCADE ); -- ============================================================================ @@ -111,81 +115,89 @@ CREATE TABLE custom_format_conditions ( -- ============================================================================ -- Link regular expressions to tags +-- Uses stable keys: regular_expression_name and tag_name CREATE TABLE regular_expression_tags ( - regular_expression_id INTEGER NOT NULL, - tag_id INTEGER NOT NULL, - PRIMARY KEY (regular_expression_id, tag_id), - FOREIGN KEY (regular_expression_id) REFERENCES regular_expressions(id) ON DELETE CASCADE, - FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE + regular_expression_name VARCHAR(100) NOT NULL, + tag_name VARCHAR(50) NOT NULL, + PRIMARY KEY (regular_expression_name, tag_name), + FOREIGN KEY (regular_expression_name) REFERENCES regular_expressions(name) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (tag_name) REFERENCES tags(name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Link custom formats to tags +-- Uses stable keys: custom_format_name and tag_name CREATE TABLE custom_format_tags ( - custom_format_id INTEGER NOT NULL, - tag_id INTEGER NOT NULL, - PRIMARY KEY (custom_format_id, tag_id), - FOREIGN KEY (custom_format_id) REFERENCES custom_formats(id) ON DELETE CASCADE, - FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE + custom_format_name VARCHAR(100) NOT NULL, + tag_name VARCHAR(50) NOT NULL, + PRIMARY KEY (custom_format_name, tag_name), + FOREIGN KEY (custom_format_name) REFERENCES custom_formats(name) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (tag_name) REFERENCES tags(name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Link quality profiles to tags +-- Uses stable keys: quality_profile_name and tag_name CREATE TABLE quality_profile_tags ( - quality_profile_id INTEGER NOT NULL, - tag_id INTEGER NOT NULL, - PRIMARY KEY (quality_profile_id, tag_id), - FOREIGN KEY (quality_profile_id) REFERENCES quality_profiles(id) ON DELETE CASCADE, - FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE + quality_profile_name VARCHAR(100) NOT NULL, + tag_name VARCHAR(50) NOT NULL, + PRIMARY KEY (quality_profile_name, tag_name), + FOREIGN KEY (quality_profile_name) REFERENCES quality_profiles(name) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (tag_name) REFERENCES tags(name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Link quality profiles to languages with type modifiers -- Type can be: 'must', 'only', 'not', or 'simple' (default language preference) +-- Uses stable keys: quality_profile_name and language_name CREATE TABLE quality_profile_languages ( - quality_profile_id INTEGER NOT NULL, - language_id INTEGER NOT NULL, + quality_profile_name VARCHAR(100) NOT NULL, + language_name VARCHAR(30) NOT NULL, type VARCHAR(20) NOT NULL DEFAULT 'simple', -- 'must', 'only', 'not', 'simple' - PRIMARY KEY (quality_profile_id, language_id), - FOREIGN KEY (quality_profile_id) REFERENCES quality_profiles(id) ON DELETE CASCADE, - FOREIGN KEY (language_id) REFERENCES languages(id) ON DELETE CASCADE + PRIMARY KEY (quality_profile_name, language_name), + FOREIGN KEY (quality_profile_name) REFERENCES quality_profiles(name) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (language_name) REFERENCES languages(name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Define which qualities belong to which quality groups -- All qualities in a group are treated as equivalent +-- Uses stable keys: (quality_profile_name, quality_group_name) and quality_name CREATE TABLE quality_group_members ( - quality_group_id INTEGER NOT NULL, - quality_id INTEGER NOT NULL, - PRIMARY KEY (quality_group_id, quality_id), - FOREIGN KEY (quality_group_id) REFERENCES quality_groups(id) ON DELETE CASCADE, - FOREIGN KEY (quality_id) REFERENCES qualities(id) ON DELETE CASCADE + quality_profile_name VARCHAR(100) NOT NULL, + quality_group_name VARCHAR(100) NOT NULL, + quality_name VARCHAR(100) NOT NULL, + PRIMARY KEY (quality_profile_name, quality_group_name, quality_name), + FOREIGN KEY (quality_profile_name, quality_group_name) REFERENCES quality_groups(quality_profile_name, name) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (quality_name) REFERENCES qualities(name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Define the quality list for a profile (ordered by position) -- Each item references either a single quality OR a quality group (never both) -- Every quality must be represented (either directly or in a group) -- The enabled flag controls whether the quality/group is active +-- Uses stable keys: quality_profile_name, quality_name, quality_group_name CREATE TABLE quality_profile_qualities ( id INTEGER PRIMARY KEY AUTOINCREMENT, - quality_profile_id INTEGER NOT NULL, - quality_id INTEGER, -- References a single quality - quality_group_id INTEGER, -- OR references a quality group + quality_profile_name VARCHAR(100) NOT NULL, + quality_name VARCHAR(100), -- References a single quality by name + quality_group_name VARCHAR(100), -- OR references a quality group by name (within this profile) position INTEGER NOT NULL, -- Display order in the profile enabled INTEGER NOT NULL DEFAULT 1, -- Whether this quality/group is enabled upgrade_until INTEGER NOT NULL DEFAULT 0, -- Stop upgrading at this quality - CHECK ((quality_id IS NOT NULL AND quality_group_id IS NULL) OR (quality_id IS NULL AND quality_group_id IS NOT NULL)), - FOREIGN KEY (quality_profile_id) REFERENCES quality_profiles(id) ON DELETE CASCADE, - FOREIGN KEY (quality_id) REFERENCES qualities(id) ON DELETE CASCADE, - FOREIGN KEY (quality_group_id) REFERENCES quality_groups(id) ON DELETE CASCADE + CHECK ((quality_name IS NOT NULL AND quality_group_name IS NULL) OR (quality_name IS NULL AND quality_group_name IS NOT NULL)), + FOREIGN KEY (quality_profile_name) REFERENCES quality_profiles(name) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (quality_name) REFERENCES qualities(name) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (quality_profile_name, quality_group_name) REFERENCES quality_groups(quality_profile_name, name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Assign custom formats to quality profiles with scoring -- Scores determine upgrade priority and filtering behavior +-- Uses stable keys: quality_profile_name and custom_format_name CREATE TABLE quality_profile_custom_formats ( - quality_profile_id INTEGER NOT NULL, - custom_format_id INTEGER NOT NULL, - arr_type VARCHAR(20) NOT NULL, -- 'radarr', 'sonarr', 'all', + quality_profile_name VARCHAR(100) NOT NULL, + custom_format_name VARCHAR(100) NOT NULL, + arr_type VARCHAR(20) NOT NULL, -- 'radarr', 'sonarr', 'all' score INTEGER NOT NULL, - PRIMARY KEY (quality_profile_id, custom_format_id, arr_type), - FOREIGN KEY (quality_profile_id) REFERENCES quality_profiles(id) ON DELETE CASCADE, - FOREIGN KEY (custom_format_id) REFERENCES custom_formats(id) ON DELETE CASCADE + PRIMARY KEY (quality_profile_name, custom_format_name, arr_type), + FOREIGN KEY (quality_profile_name) REFERENCES quality_profiles(name) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (custom_format_name) REFERENCES custom_formats(name) ON DELETE CASCADE ON UPDATE CASCADE ); -- ============================================================================ @@ -196,71 +208,98 @@ CREATE TABLE quality_profile_custom_formats ( -- Pattern-based conditions (release_title, release_group, edition) -- Each pattern condition references exactly one regular expression +-- Uses stable keys: (custom_format_name, condition_name) and regular_expression_name CREATE TABLE condition_patterns ( - custom_format_condition_id INTEGER PRIMARY KEY, - regular_expression_id INTEGER NOT NULL, - FOREIGN KEY (custom_format_condition_id) REFERENCES custom_format_conditions(id) ON DELETE CASCADE, - FOREIGN KEY (regular_expression_id) REFERENCES regular_expressions(id) ON DELETE CASCADE + custom_format_name VARCHAR(100) NOT NULL, + condition_name VARCHAR(100) NOT NULL, + regular_expression_name VARCHAR(100) NOT NULL, + PRIMARY KEY (custom_format_name, condition_name), + FOREIGN KEY (custom_format_name, condition_name) REFERENCES custom_format_conditions(custom_format_name, name) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (regular_expression_name) REFERENCES regular_expressions(name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Language-based conditions +-- Uses stable keys: (custom_format_name, condition_name) and language_name CREATE TABLE condition_languages ( - custom_format_condition_id INTEGER PRIMARY KEY, - language_id INTEGER NOT NULL, + custom_format_name VARCHAR(100) NOT NULL, + condition_name VARCHAR(100) NOT NULL, + language_name VARCHAR(30) NOT NULL, except_language INTEGER NOT NULL DEFAULT 0, -- Match everything EXCEPT this language - FOREIGN KEY (custom_format_condition_id) REFERENCES custom_format_conditions(id) ON DELETE CASCADE, - FOREIGN KEY (language_id) REFERENCES languages(id) ON DELETE CASCADE + PRIMARY KEY (custom_format_name, condition_name), + FOREIGN KEY (custom_format_name, condition_name) REFERENCES custom_format_conditions(custom_format_name, name) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (language_name) REFERENCES languages(name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Indexer flag conditions (e.g., "Scene", "Freeleech") +-- Uses stable key: (custom_format_name, condition_name) CREATE TABLE condition_indexer_flags ( - custom_format_condition_id INTEGER PRIMARY KEY, + custom_format_name VARCHAR(100) NOT NULL, + condition_name VARCHAR(100) NOT NULL, flag VARCHAR(100) NOT NULL, - FOREIGN KEY (custom_format_condition_id) REFERENCES custom_format_conditions(id) ON DELETE CASCADE + PRIMARY KEY (custom_format_name, condition_name), + FOREIGN KEY (custom_format_name, condition_name) REFERENCES custom_format_conditions(custom_format_name, name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Source conditions (e.g., "Bluray", "Web", "DVD") +-- Uses stable key: (custom_format_name, condition_name) CREATE TABLE condition_sources ( - custom_format_condition_id INTEGER PRIMARY KEY, + custom_format_name VARCHAR(100) NOT NULL, + condition_name VARCHAR(100) NOT NULL, source VARCHAR(100) NOT NULL, - FOREIGN KEY (custom_format_condition_id) REFERENCES custom_format_conditions(id) ON DELETE CASCADE + PRIMARY KEY (custom_format_name, condition_name), + FOREIGN KEY (custom_format_name, condition_name) REFERENCES custom_format_conditions(custom_format_name, name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Resolution conditions (e.g., "1080p", "2160p") +-- Uses stable key: (custom_format_name, condition_name) CREATE TABLE condition_resolutions ( - custom_format_condition_id INTEGER PRIMARY KEY, + custom_format_name VARCHAR(100) NOT NULL, + condition_name VARCHAR(100) NOT NULL, resolution VARCHAR(100) NOT NULL, - FOREIGN KEY (custom_format_condition_id) REFERENCES custom_format_conditions(id) ON DELETE CASCADE + PRIMARY KEY (custom_format_name, condition_name), + FOREIGN KEY (custom_format_name, condition_name) REFERENCES custom_format_conditions(custom_format_name, name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Quality modifier conditions (e.g., "REMUX", "WEBDL") +-- Uses stable key: (custom_format_name, condition_name) CREATE TABLE condition_quality_modifiers ( - custom_format_condition_id INTEGER PRIMARY KEY, + custom_format_name VARCHAR(100) NOT NULL, + condition_name VARCHAR(100) NOT NULL, quality_modifier VARCHAR(100) NOT NULL, - FOREIGN KEY (custom_format_condition_id) REFERENCES custom_format_conditions(id) ON DELETE CASCADE + PRIMARY KEY (custom_format_name, condition_name), + FOREIGN KEY (custom_format_name, condition_name) REFERENCES custom_format_conditions(custom_format_name, name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Size-based conditions with min/max bounds in bytes +-- Uses stable key: (custom_format_name, condition_name) CREATE TABLE condition_sizes ( - custom_format_condition_id INTEGER PRIMARY KEY, + custom_format_name VARCHAR(100) NOT NULL, + condition_name VARCHAR(100) NOT NULL, min_bytes INTEGER, -- Null means no minimum max_bytes INTEGER, -- Null means no maximum - FOREIGN KEY (custom_format_condition_id) REFERENCES custom_format_conditions(id) ON DELETE CASCADE + PRIMARY KEY (custom_format_name, condition_name), + FOREIGN KEY (custom_format_name, condition_name) REFERENCES custom_format_conditions(custom_format_name, name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Release type conditions (e.g., "Movie", "Episode") +-- Uses stable key: (custom_format_name, condition_name) CREATE TABLE condition_release_types ( - custom_format_condition_id INTEGER PRIMARY KEY, + custom_format_name VARCHAR(100) NOT NULL, + condition_name VARCHAR(100) NOT NULL, release_type VARCHAR(100) NOT NULL, - FOREIGN KEY (custom_format_condition_id) REFERENCES custom_format_conditions(id) ON DELETE CASCADE + PRIMARY KEY (custom_format_name, condition_name), + FOREIGN KEY (custom_format_name, condition_name) REFERENCES custom_format_conditions(custom_format_name, name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Year-based conditions with min/max bounds +-- Uses stable key: (custom_format_name, condition_name) CREATE TABLE condition_years ( - custom_format_condition_id INTEGER PRIMARY KEY, + custom_format_name VARCHAR(100) NOT NULL, + condition_name VARCHAR(100) NOT NULL, min_year INTEGER, -- Null means no minimum max_year INTEGER, -- Null means no maximum - FOREIGN KEY (custom_format_condition_id) REFERENCES custom_format_conditions(id) ON DELETE CASCADE + PRIMARY KEY (custom_format_name, condition_name), + FOREIGN KEY (custom_format_name, condition_name) REFERENCES custom_format_conditions(custom_format_name, name) ON DELETE CASCADE ON UPDATE CASCADE ); -- ============================================================================ @@ -269,16 +308,17 @@ CREATE TABLE condition_years ( -- Test cases for validating custom format matching logic -- Each test belongs to a custom format and specifies whether a title should match +-- Uses stable key: custom_format_name CREATE TABLE custom_format_tests ( id INTEGER PRIMARY KEY AUTOINCREMENT, - custom_format_id INTEGER NOT NULL, + custom_format_name VARCHAR(100) NOT NULL, title TEXT NOT NULL, -- Release title to test against type VARCHAR(20) NOT NULL, -- 'movie' or 'series' should_match INTEGER NOT NULL, -- 1 = should match, 0 = should not match description TEXT, -- Why this test exists / edge case covered created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE(custom_format_id, title, type), - FOREIGN KEY (custom_format_id) REFERENCES custom_formats(id) ON DELETE CASCADE + UNIQUE(custom_format_name, title, type), + FOREIGN KEY (custom_format_name) REFERENCES custom_formats(name) ON DELETE CASCADE ON UPDATE CASCADE ); -- ============================================================================ @@ -286,25 +326,27 @@ CREATE TABLE custom_format_tests ( -- ============================================================================ -- Radarr quality size definitions +-- Uses stable key: quality_name CREATE TABLE radarr_quality_definitions ( - quality_id INTEGER PRIMARY KEY, + quality_name VARCHAR(100) PRIMARY KEY, min_size INTEGER NOT NULL DEFAULT 0, max_size INTEGER NOT NULL, preferred_size INTEGER NOT NULL, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (quality_id) REFERENCES qualities(id) ON DELETE CASCADE + FOREIGN KEY (quality_name) REFERENCES qualities(name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Sonarr quality size definitions +-- Uses stable key: quality_name CREATE TABLE sonarr_quality_definitions ( - quality_id INTEGER PRIMARY KEY, + quality_name VARCHAR(100) PRIMARY KEY, min_size INTEGER NOT NULL DEFAULT 0, max_size INTEGER NOT NULL, preferred_size INTEGER NOT NULL, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (quality_id) REFERENCES qualities(id) ON DELETE CASCADE + FOREIGN KEY (quality_name) REFERENCES qualities(name) ON DELETE CASCADE ON UPDATE CASCADE ); -- Radarr naming configuration @@ -390,12 +432,13 @@ CREATE TABLE delay_profiles ( ); -- Link delay profiles to tags (at least 1 required - enforced at application level) +-- Uses stable keys: delay_profile_name and tag_name CREATE TABLE delay_profile_tags ( - delay_profile_id INTEGER NOT NULL, - tag_id INTEGER NOT NULL, - PRIMARY KEY (delay_profile_id, tag_id), - FOREIGN KEY (delay_profile_id) REFERENCES delay_profiles(id) ON DELETE CASCADE, - FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE + delay_profile_name VARCHAR(100) NOT NULL, + tag_name VARCHAR(50) NOT NULL, + PRIMARY KEY (delay_profile_name, tag_name), + FOREIGN KEY (delay_profile_name) REFERENCES delay_profiles(name) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (tag_name) REFERENCES tags(name) ON DELETE CASCADE ON UPDATE CASCADE ); -- ============================================================================ @@ -439,5 +482,5 @@ CREATE INDEX idx_test_releases_entity ON test_releases(entity_type, entity_tmdb_ -- Ensure only one quality item per profile can be marked as upgrade_until CREATE UNIQUE INDEX idx_one_upgrade_until_per_profile -ON quality_profile_qualities(quality_profile_id) +ON quality_profile_qualities(quality_profile_name) WHERE upgrade_until = 1;