-- Migration 117: partial composite index to accelerate deactivate_stale_listings
--
-- Problem:
--   deactivate_stale_listings executes:
--     UPDATE listings SET is_active = false
--     WHERE source = :x
--       AND is_active = true
--       AND last_seen_at < NOW() - interval '...'
--       [AND listing_segment = ANY(...)]
--   With ~39k rows and no index on last_seen_at or is_active, every call
--   performs a sequential scan.
--
-- Fix:
--   A partial B-tree index on (source, last_seen_at) WHERE is_active.
--   The WHERE is_active partial predicate eliminates already-deactivated rows
--   (typically the majority after steady-state), keeping the index small.
--   The (source, last_seen_at) composite matches the equality+range predicates
--   in the deactivate query so the planner can use an index scan.
--
-- Note on CONCURRENTLY:
--   NOT used here. The deploy migration runner wraps each file in an explicit
--   transaction (BEGIN/COMMIT). CREATE INDEX CONCURRENTLY cannot run inside
--   a transaction block. Plain CREATE INDEX holds an ACCESS SHARE lock;
--   at ~39k rows the lock duration is sub-second and acceptable.
--
-- Idempotent: IF NOT EXISTS makes repeated runs safe.
-- Deploy order: standalone.

BEGIN;

CREATE INDEX IF NOT EXISTS listings_active_source_lastseen_idx
    ON listings (source, last_seen_at)
    WHERE is_active;

COMMIT;
