-- ===================================================== -- Data Integrity Constraints for Courses, Events, Bookings -- -- Adds CHECK constraints and partial unique indexes to -- enforce business rules at the database level. -- -- All constraint additions are idempotent — wrapped in -- DO blocks that check pg_constraint before adding. -- ===================================================== -- ------------------------------------------------------- -- COURSES -- ------------------------------------------------------- -- reduced_fee must not exceed fee DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'chk_courses_reduced_fee_lte_fee' ) THEN ALTER TABLE public.courses ADD CONSTRAINT chk_courses_reduced_fee_lte_fee CHECK (reduced_fee IS NULL OR reduced_fee <= fee); END IF; END; $$; -- min_participants must not exceed capacity DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'chk_courses_min_lte_capacity' ) THEN ALTER TABLE public.courses ADD CONSTRAINT chk_courses_min_lte_capacity CHECK (min_participants IS NULL OR min_participants <= capacity); END IF; END; $$; -- end_date must be on or after start_date DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'chk_courses_date_range' ) THEN ALTER TABLE public.courses ADD CONSTRAINT chk_courses_date_range CHECK (end_date IS NULL OR start_date IS NULL OR end_date >= start_date); END IF; END; $$; -- registration_deadline must be on or before start_date DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'chk_courses_deadline_before_start' ) THEN ALTER TABLE public.courses ADD CONSTRAINT chk_courses_deadline_before_start CHECK (registration_deadline IS NULL OR start_date IS NULL OR registration_deadline <= start_date); END IF; END; $$; -- Unique course_number per account (partial index — allows NULLs and empty strings) CREATE UNIQUE INDEX IF NOT EXISTS uix_courses_number_per_account ON public.courses(account_id, course_number) WHERE course_number IS NOT NULL AND course_number != ''; -- ------------------------------------------------------- -- EVENTS -- ------------------------------------------------------- -- min_age must not exceed max_age DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'chk_events_age_range' ) THEN ALTER TABLE public.events ADD CONSTRAINT chk_events_age_range CHECK (min_age IS NULL OR max_age IS NULL OR min_age <= max_age); END IF; END; $$; -- end_date must be on or after event_date DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'chk_events_date_range' ) THEN ALTER TABLE public.events ADD CONSTRAINT chk_events_date_range CHECK (end_date IS NULL OR end_date >= event_date); END IF; END; $$; -- registration_deadline must be on or before event_date DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'chk_events_deadline_before_event' ) THEN ALTER TABLE public.events ADD CONSTRAINT chk_events_deadline_before_event CHECK (registration_deadline IS NULL OR registration_deadline <= event_date); END IF; END; $$; -- ------------------------------------------------------- -- BOOKINGS -- ------------------------------------------------------- -- At least 1 adult required DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'chk_bookings_min_adults' ) THEN ALTER TABLE public.bookings ADD CONSTRAINT chk_bookings_min_adults CHECK (adults >= 1); END IF; END; $$; -- total_price must be non-negative DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'chk_bookings_price_non_negative' ) THEN ALTER TABLE public.bookings ADD CONSTRAINT chk_bookings_price_non_negative CHECK (total_price >= 0); END IF; END; $$;