From 17c012316117362e8cba9bc5f5c036b27af84b33 Mon Sep 17 00:00:00 2001 From: Stephanos A Date: Thu, 16 Jul 2026 21:10:02 +0300 Subject: [PATCH] Add unique constraint to JourneySegment for seat bookings per schedule --- .../migration.sql | 18 ++++++++++++++++++ apps/edr-passenger-api/prisma/schema.prisma | 4 ++++ 2 files changed, 22 insertions(+) create mode 100644 apps/edr-passenger-api/prisma/migrations/20260716202849_add_journey_segment_unique_seat_per_schedule/migration.sql diff --git a/apps/edr-passenger-api/prisma/migrations/20260716202849_add_journey_segment_unique_seat_per_schedule/migration.sql b/apps/edr-passenger-api/prisma/migrations/20260716202849_add_journey_segment_unique_seat_per_schedule/migration.sql new file mode 100644 index 000000000..2608b4cbf --- /dev/null +++ b/apps/edr-passenger-api/prisma/migrations/20260716202849_add_journey_segment_unique_seat_per_schedule/migration.sql @@ -0,0 +1,18 @@ +-- Remove duplicate JourneySegment rows, keeping the one with the lowest id +-- (earliest created) per (scheduleId, seatId, departureStationId) group. +-- This cleans up any existing double-bookings before the unique index is applied. +DELETE FROM passenger."JourneySegment" +WHERE id NOT IN ( + SELECT MIN(id) + FROM passenger."JourneySegment" + WHERE "seatId" IS NOT NULL + GROUP BY "scheduleId", "seatId", "departureStationId" +) +AND "seatId" IS NOT NULL; + +-- Prevents two confirmed bookings from occupying the same seat on the same +-- schedule hop — the hard DB backstop against application-level race conditions. +-- Partial index: seatId IS NOT NULL excludes free-child rows that have no seat. +CREATE UNIQUE INDEX "JourneySegment_scheduleId_seatId_departureStationId_key" +ON passenger."JourneySegment" ("scheduleId", "seatId", "departureStationId") +WHERE "seatId" IS NOT NULL; diff --git a/apps/edr-passenger-api/prisma/schema.prisma b/apps/edr-passenger-api/prisma/schema.prisma index d2dc44751..ccc68915d 100644 --- a/apps/edr-passenger-api/prisma/schema.prisma +++ b/apps/edr-passenger-api/prisma/schema.prisma @@ -991,6 +991,10 @@ model JourneySegment { arrivalStationId String journey Journey @relation(fields: [journeyId], references: [id]) schedule TrainSchedule @relation(fields: [scheduleId], references: [id]) + + // Prevents two confirmed bookings from occupying the same seat on the same + // schedule hop — the hard DB backstop against application-level race conditions. + @@unique([scheduleId, seatId, departureStationId]) @@schema("passenger") }