-- 003_faulty_column.sql -- Adds the operator-controlled faulty flag to the positions hypertable, -- plus the partial index that optimises the processor's hot-path read. -- -- Why this is a separate file from 002: -- The faulty column is an operator-plane concern layered on top of the -- hypertable's initial shape. Keeping it in its own migration makes the -- evolution visible in git history — an operator or analyst reading the -- migration log can see that positions started as a pure telemetry store -- and the quality-control flag was added as a deliberate, dated step. -- It also keeps migration 002 as the authoritative "what processor writes" -- definition, uncluttered by the downstream read/flag concern. -- -- Column semantics: -- faulty BOOLEAN NOT NULL DEFAULT FALSE -- The column is operator-controlled; it is NEVER set by the [[processor]] -- writer. The processor always inserts rows with faulty = FALSE (via the -- DEFAULT — the column is intentionally omitted from INSERT statements). -- A track operator flips the flag through [[directus]] when a recorded -- position is unrealistic (jumpy GPS, impossible speed/coordinate). -- When the flag is set, Directus emits a webhook to the -- recompute:requests Redis stream; the processor re-evaluates any -- entry_penalties whose window overlaps the flagged position's timestamp. -- -- Index strategy: -- positions_faulty_idx is a PARTIAL index covering only rows where -- faulty = FALSE. This matches the processor's hot-path read pattern: -- all evaluators (peak-speed, crossing detection, replay recompute) filter -- WHERE faulty = FALSE. The partial index is smaller than a full index, -- fits better in shared_buffers, and is never consulted for operator -- queries that explicitly look at faulty rows — those use the broader -- positions_device_ts index from migration 002. ALTER TABLE positions ADD COLUMN IF NOT EXISTS faulty boolean NOT NULL DEFAULT FALSE; -- Partial index: covers the processor's standard read path (faulty = FALSE). -- Column order (device_id, ts DESC) supports per-device range queries -- returning most-recent-first, which is the dominant access pattern for -- peak-speed evaluation and crossing detection. CREATE INDEX IF NOT EXISTS positions_faulty_idx ON positions (device_id, ts DESC) WHERE faulty = FALSE; -- ------------------------------------------------------------------------- -- Assertion block: verify the column and index are present with the expected -- shape. Catches drift where stage already has a faulty column but with a -- different type or a missing DEFAULT (ADD COLUMN IF NOT EXISTS is a no-op -- against an existing column regardless of its definition). -- ------------------------------------------------------------------------- DO $$ DECLARE _col_type text; _col_notnull boolean; _col_default text; BEGIN -- 1. Column exists with correct type SELECT data_type, is_nullable = 'NO', column_default INTO _col_type, _col_notnull, _col_default FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'positions' AND column_name = 'faulty'; IF _col_type IS NULL THEN RAISE EXCEPTION 'positions.faulty column is missing after migration 003'; END IF; IF _col_type IS DISTINCT FROM 'boolean' THEN RAISE EXCEPTION 'positions.faulty expected type boolean, found %', _col_type; END IF; IF NOT _col_notnull THEN RAISE EXCEPTION 'positions.faulty must be NOT NULL but is nullable — schema drift'; END IF; -- DEFAULT is stored as a normalised expression string; Postgres represents -- FALSE as 'false' in information_schema (lower-case, no quotes). IF _col_default IS DISTINCT FROM 'false' THEN RAISE EXCEPTION 'positions.faulty expected DEFAULT false, found %', coalesce(_col_default, 'NULL (no default)'); END IF; -- 2. Partial index exists IF NOT EXISTS ( SELECT 1 FROM pg_indexes WHERE schemaname = 'public' AND tablename = 'positions' AND indexname = 'positions_faulty_idx' ) THEN RAISE EXCEPTION 'partial index positions_faulty_idx is missing from positions'; END IF; -- 3. Partial index has the expected WHERE predicate -- pg_indexes.indexdef includes the full CREATE INDEX statement as text; -- the predicate appears as "WHERE (faulty = false)". IF NOT EXISTS ( SELECT 1 FROM pg_indexes WHERE schemaname = 'public' AND tablename = 'positions' AND indexname = 'positions_faulty_idx' AND indexdef ILIKE '%where (faulty = false)%' ) THEN RAISE EXCEPTION 'positions_faulty_idx exists but does not have the expected predicate "WHERE (faulty = false)" — check indexdef in pg_indexes'; END IF; END $$;