-- ============================================================================ -- Performance & Missing Indexes -- ============================================================================ -- scheduled_events: index on created_at for list ordering CREATE INDEX IF NOT EXISTS idx_scheduled_events_created_at ON scheduled_events(created_at DESC); -- scheduled_events: composite index for common user+status queries CREATE INDEX IF NOT EXISTS idx_scheduled_events_user_status ON scheduled_events(user_id, status); -- messages: index on sender_id for delete-by-sender queries CREATE INDEX IF NOT EXISTS idx_messages_sender ON messages(sender_id); -- messages: composite for conversation listing CREATE INDEX IF NOT EXISTS idx_messages_conversation_sender ON messages(conversation_id, sender_id); -- notifications: composite unread badge query CREATE INDEX IF NOT EXISTS idx_notifications_user_read ON notifications(user_id, is_read, created_at DESC); -- ai_conversations: composite user+created query CREATE INDEX IF NOT EXISTS idx_ai_conversations_user_created ON ai_conversations(user_id, created_at DESC); -- login_attempts: TTL cleanup CREATE INDEX IF NOT EXISTS idx_login_attempts_cleanup ON login_attempts(attempted_at) WHERE was_successful = false; -- facebook_scrape_logs: composite account+created index (replaces separate one) DROP INDEX IF EXISTS idx_fb_scrape_logs_account; CREATE INDEX IF NOT EXISTS idx_fb_scrape_logs_account_created ON facebook_scrape_logs(account_id, created_at DESC); -- lead_conversions: composite lead+customer (replaces two separate indexes) CREATE INDEX IF NOT EXISTS idx_lead_conversions_lead_customer ON lead_conversions(lead_id, customer_id); -- ============================================================================ -- Cache current_user_hierarchy_level as session variable for RLS performance -- ============================================================================ CREATE OR REPLACE FUNCTION set_session_user_context(p_user_id UUID) RETURNS VOID AS $$ DECLARE level INT; BEGIN SELECT MIN(r.hierarchy_level) INTO level FROM user_roles ur JOIN roles r ON r.id = ur.role_id WHERE ur.user_id = p_user_id; PERFORM set_config('app.current_user_id', p_user_id::TEXT, true); PERFORM set_config('app.current_user_hierarchy_level', COALESCE(level::TEXT, ''), true); END; $$ LANGUAGE plpgsql SECURITY DEFINER; -- Update current_user_hierarchy_level() to use the cached session variable CREATE OR REPLACE FUNCTION current_user_hierarchy_level() RETURNS INT AS $$ DECLARE level_text TEXT; BEGIN BEGIN level_text := NULLIF(current_setting('app.current_user_hierarchy_level', true), ''); IF level_text IS NOT NULL THEN RETURN level_text::INT; END IF; EXCEPTION WHEN OTHERS THEN NULL; END; RETURN NULL; END; $$ LANGUAGE plpgsql STABLE;