From 9ec8535ff73587b9da446b89494b9b07ba391f45 Mon Sep 17 00:00:00 2001 From: StefanMoCoAt Date: Sat, 28 Mar 2026 20:52:58 +0100 Subject: [PATCH] Enforce natural key uniqueness by adding unique constraints to Bundesland, Platz, Altersklasse, and Land tables. --- ...005__Add_Unique_Constraints_Masterdata.sql | 46 +++++++++++++++++++ .../persistence/BundeslandTable.kt | 3 ++ .../infrastructure/persistence/PlatzTable.kt | 2 + 3 files changed, 51 insertions(+) create mode 100644 backend/services/masterdata/masterdata-api/src/main/resources/db/migration/V005__Add_Unique_Constraints_Masterdata.sql diff --git a/backend/services/masterdata/masterdata-api/src/main/resources/db/migration/V005__Add_Unique_Constraints_Masterdata.sql b/backend/services/masterdata/masterdata-api/src/main/resources/db/migration/V005__Add_Unique_Constraints_Masterdata.sql new file mode 100644 index 00000000..1d48672a --- /dev/null +++ b/backend/services/masterdata/masterdata-api/src/main/resources/db/migration/V005__Add_Unique_Constraints_Masterdata.sql @@ -0,0 +1,46 @@ +-- Enforce natural key uniqueness for master-data upserts + +-- Bundesland: unique (land_id, kuerzel) for non-null kuerzel values +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_indexes WHERE schemaname = 'public' AND indexname = 'ux_bundesland_land_kuerzel' + ) THEN + CREATE UNIQUE INDEX ux_bundesland_land_kuerzel + ON bundesland(land_id, kuerzel) + WHERE kuerzel IS NOT NULL; + END IF; +END$$; + +-- Platz: unique (turnier_id, name) +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_indexes WHERE schemaname = 'public' AND indexname = 'ux_platz_turnier_name' + ) THEN + CREATE UNIQUE INDEX ux_platz_turnier_name + ON platz(turnier_id, name); + END IF; +END$$; + +-- Altersklasse: unique (altersklasse_code) – ensure index exists +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_indexes WHERE schemaname = 'public' AND indexname = 'altersklasse_altersklasse_code_unique' + ) THEN + CREATE UNIQUE INDEX altersklasse_altersklasse_code_unique + ON altersklasse(altersklasse_code); + END IF; +END$$; + +-- Land: unique (iso_alpha3_code) – ensure index exists +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_indexes WHERE schemaname = 'public' AND indexname = 'land_iso_alpha3_code_unique' + ) THEN + CREATE UNIQUE INDEX land_iso_alpha3_code_unique + ON land(iso_alpha3_code); + END IF; +END$$; diff --git a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/BundeslandTable.kt b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/BundeslandTable.kt index 079e137f..4349d910 100644 --- a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/BundeslandTable.kt +++ b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/BundeslandTable.kt @@ -27,5 +27,8 @@ object BundeslandTable : Table("bundesland") { init { uniqueIndex("idx_bundesland_oeps", oepsCode, landId) uniqueIndex("idx_bundesland_iso", iso3166_2_Code) + // Natürlicher Schlüssel gem. Architektur: (land_id + kuerzel) eindeutig + // Hinweis: kuerzel kann NULL sein – der Unique-Index greift dann nur für nicht-null Werte pro Land. + uniqueIndex("ux_bundesland_land_kuerzel", landId, kuerzel) } } diff --git a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/PlatzTable.kt b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/PlatzTable.kt index 169adb84..bd9cdce6 100644 --- a/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/PlatzTable.kt +++ b/backend/services/masterdata/masterdata-infrastructure/src/main/kotlin/at/mocode/masterdata/infrastructure/persistence/PlatzTable.kt @@ -25,5 +25,7 @@ object PlatzTable : Table("platz") { init { index("idx_platz_turnier", isUnique = false, turnierId) + // Natürlicher Schlüssel gem. Architektur: (turnier_id + name) eindeutig + uniqueIndex("ux_platz_turnier_name", turnierId, name) } }