-- 050_data_validation_constraints.sql -- Data Validation Constraints (BE-DB-011: Add database constraints for data validation) -- Add CHECK constraints for status values, enum constraints -- Note: PostgreSQL doesn't support IF NOT EXISTS with ADD CONSTRAINT, so we use DO blocks -- === TRACKS STATUS CONSTRAINTS === -- Constraint for track status (uploading, processing, completed, failed) DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'tracks' AND constraint_name = 'chk_tracks_status_valid' ) THEN ALTER TABLE public.tracks ADD CONSTRAINT chk_tracks_status_valid CHECK (status IN ('uploading', 'processing', 'completed', 'failed')); END IF; END $$; -- Constraint for stream_status (pending, processing, ready, error) DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'tracks' AND constraint_name = 'chk_tracks_stream_status_valid' ) THEN ALTER TABLE public.tracks ADD CONSTRAINT chk_tracks_stream_status_valid CHECK (stream_status IN ('pending', 'processing', 'ready', 'error')); END IF; END $$; -- Constraint for counts (non-negative) DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'tracks' AND constraint_name = 'chk_tracks_counts_non_negative' ) THEN ALTER TABLE public.tracks ADD CONSTRAINT chk_tracks_counts_non_negative CHECK (play_count >= 0 AND like_count >= 0 AND comment_count >= 0 AND download_count >= 0); END IF; END $$; -- Constraint for year (reasonable range) DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'tracks' AND constraint_name = 'chk_tracks_year_valid' ) THEN ALTER TABLE public.tracks ADD CONSTRAINT chk_tracks_year_valid CHECK (year >= 0 AND year <= EXTRACT(YEAR FROM CURRENT_DATE) + 10); END IF; END $$; -- === PLAYLISTS CONSTRAINTS === -- Constraint for counts (non-negative) DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'playlists' AND constraint_name = 'chk_playlists_counts_non_negative' ) THEN ALTER TABLE public.playlists ADD CONSTRAINT chk_playlists_counts_non_negative CHECK (track_count >= 0 AND follower_count >= 0); END IF; END $$; -- === PLAYLIST_TRACKS CONSTRAINTS === -- Constraint for position (positive) DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'playlist_tracks' AND constraint_name = 'chk_playlist_tracks_position_positive' ) THEN ALTER TABLE public.playlist_tracks ADD CONSTRAINT chk_playlist_tracks_position_positive CHECK (position >= 0); END IF; END $$; -- === HLSTranscodeQueue CONSTRAINTS === -- Constraint for queue status (pending, processing, completed, failed) DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'hls_transcode_queue' AND constraint_name = 'chk_hls_transcode_queue_status_valid' ) THEN ALTER TABLE public.hls_transcode_queue ADD CONSTRAINT chk_hls_transcode_queue_status_valid CHECK (status IN ('pending', 'processing', 'completed', 'failed')); END IF; END $$; -- Constraint for retry counts (non-negative) DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'hls_transcode_queue' AND constraint_name = 'chk_hls_transcode_queue_retry_valid' ) THEN ALTER TABLE public.hls_transcode_queue ADD CONSTRAINT chk_hls_transcode_queue_retry_valid CHECK (retry_count >= 0 AND max_retries >= 0); END IF; END $$; -- Constraint for priority (reasonable range) DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'hls_transcode_queue' AND constraint_name = 'chk_hls_transcode_queue_priority_valid' ) THEN ALTER TABLE public.hls_transcode_queue ADD CONSTRAINT chk_hls_transcode_queue_priority_valid CHECK (priority >= 1 AND priority <= 10); END IF; END $$; -- === TRACK_COMMENTS CONSTRAINTS === -- Constraint for content length (already exists, but ensure it's there) -- Note: chk_track_comments_content_length already exists in 041_streaming_analytics.sql -- === TRACK_PLAYS CONSTRAINTS === -- Constraint for duration (non-negative) DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'track_plays' AND constraint_name = 'chk_track_plays_duration_non_negative' ) THEN ALTER TABLE public.track_plays ADD CONSTRAINT chk_track_plays_duration_non_negative CHECK (duration >= 0); END IF; END $$; -- === PLAYBACK_ANALYTICS CONSTRAINTS === -- Note: playback_analytics table may not exist yet (referenced in views but not created) -- Constraint for completion_rate (0-100) DO $$ BEGIN -- Check if table exists before adding constraints IF EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'playback_analytics' ) AND NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'playback_analytics' AND constraint_name = 'chk_playback_analytics_completion_rate' ) THEN ALTER TABLE public.playback_analytics ADD CONSTRAINT chk_playback_analytics_completion_rate CHECK (completion_rate >= 0 AND completion_rate <= 100); END IF; END $$; -- Constraint for counts (non-negative) DO $$ BEGIN -- Check if table exists before adding constraints IF EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'playback_analytics' ) AND NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'playback_analytics' AND constraint_name = 'chk_playback_analytics_counts_non_negative' ) THEN ALTER TABLE public.playback_analytics ADD CONSTRAINT chk_playback_analytics_counts_non_negative CHECK (play_time >= 0 AND pause_count >= 0 AND seek_count >= 0); END IF; END $$; -- === PLAYLIST_VERSION CONSTRAINTS === -- Note: playlist_versions table may not exist yet -- Constraint for version (positive) DO $$ BEGIN -- Check if table exists before adding constraints IF EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'playlist_versions' ) AND NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'playlist_versions' AND constraint_name = 'chk_playlist_versions_version_positive' ) THEN ALTER TABLE public.playlist_versions ADD CONSTRAINT chk_playlist_versions_version_positive CHECK (version > 0); END IF; END $$; -- Constraint for action (created, updated, restored) DO $$ BEGIN -- Check if table exists before adding constraints IF EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'playlist_versions' ) AND NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'playlist_versions' AND constraint_name = 'chk_playlist_versions_action_valid' ) THEN ALTER TABLE public.playlist_versions ADD CONSTRAINT chk_playlist_versions_action_valid CHECK (action IN ('created', 'updated', 'restored')); END IF; END $$; -- === TRACK_HISTORY CONSTRAINTS === -- Constraint for action (created, updated, deleted, published, unpublished, restored) DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'track_history' AND constraint_name = 'chk_track_history_action_valid' ) THEN ALTER TABLE public.track_history ADD CONSTRAINT chk_track_history_action_valid CHECK (action IN ('created', 'updated', 'deleted', 'published', 'unpublished', 'restored')); END IF; END $$; -- === BITRATE_ADAPTATION CONSTRAINTS === -- Constraint for reason (network_slow, network_fast, user_selected, buffer_low) DO $$ BEGIN -- Check if table exists before adding constraints IF EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'bitrate_adaptation_logs' ) AND NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'bitrate_adaptation_logs' AND constraint_name = 'chk_bitrate_adaptation_reason_valid' ) THEN ALTER TABLE public.bitrate_adaptation_logs ADD CONSTRAINT chk_bitrate_adaptation_reason_valid CHECK (reason IN ('network_slow', 'network_fast', 'user_selected', 'buffer_low')); END IF; END $$; -- Constraint for bitrates (positive) DO $$ BEGIN -- Check if table exists before adding constraints IF EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'bitrate_adaptation_logs' ) AND NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'bitrate_adaptation_logs' AND constraint_name = 'chk_bitrate_adaptation_bitrates_positive' ) THEN ALTER TABLE public.bitrate_adaptation_logs ADD CONSTRAINT chk_bitrate_adaptation_bitrates_positive CHECK (old_bitrate > 0 AND new_bitrate > 0); END IF; END $$; -- === NOTIFICATIONS CONSTRAINTS === -- Constraint for type (reasonable length) DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'notifications' AND constraint_name = 'chk_notifications_type_length' ) THEN ALTER TABLE public.notifications ADD CONSTRAINT chk_notifications_type_length CHECK (LENGTH(type) >= 1 AND LENGTH(type) <= 50); END IF; END $$; -- Constraint for title (reasonable length) DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'notifications' AND constraint_name = 'chk_notifications_title_length' ) THEN ALTER TABLE public.notifications ADD CONSTRAINT chk_notifications_title_length CHECK (LENGTH(title) >= 1 AND LENGTH(title) <= 255); END IF; END $$; -- === USER_PROFILES CONSTRAINTS === -- Constraint for counts (non-negative) DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'user_profiles' AND constraint_name = 'chk_user_profiles_counts_non_negative' ) THEN ALTER TABLE public.user_profiles ADD CONSTRAINT chk_user_profiles_counts_non_negative CHECK (follower_count >= 0 AND following_count >= 0 AND track_count >= 0 AND playlist_count >= 0); END IF; END $$; -- Comments (only add comments if constraints exist) DO $$ BEGIN IF EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'tracks' AND constraint_name = 'chk_tracks_status_valid' ) THEN COMMENT ON CONSTRAINT chk_tracks_status_valid ON public.tracks IS 'Validates track status values'; END IF; IF EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'tracks' AND constraint_name = 'chk_tracks_stream_status_valid' ) THEN COMMENT ON CONSTRAINT chk_tracks_stream_status_valid ON public.tracks IS 'Validates stream status values'; END IF; IF EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'hls_transcode_queue' AND constraint_name = 'chk_hls_transcode_queue_status_valid' ) THEN COMMENT ON CONSTRAINT chk_hls_transcode_queue_status_valid ON public.hls_transcode_queue IS 'Validates queue status values'; END IF; IF EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'playlist_versions' AND constraint_name = 'chk_playlist_versions_action_valid' ) THEN COMMENT ON CONSTRAINT chk_playlist_versions_action_valid ON public.playlist_versions IS 'Validates playlist version action values'; END IF; IF EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'track_history' AND constraint_name = 'chk_track_history_action_valid' ) THEN COMMENT ON CONSTRAINT chk_track_history_action_valid ON public.track_history IS 'Validates track history action values'; END IF; IF EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema = 'public' AND table_name = 'bitrate_adaptation_logs' AND constraint_name = 'chk_bitrate_adaptation_reason_valid' ) THEN COMMENT ON CONSTRAINT chk_bitrate_adaptation_reason_valid ON public.bitrate_adaptation_logs IS 'Validates bitrate adaptation reason values'; END IF; END $$;