sql/119: product_type enum (module|dac|aoc|aec|switch|nic|accessory) computed by trigger trg_transceivers_biu from part_number/category/reach on every insert/update. Backfilled 41008 rows (36284 module, 2791 dac, 1835 aoc, 29 aec, 27 accessory, 26 switch, 16 nic). Cables and switches/NICs no longer counted as modules; BASE-T/RJ45 copper modules protected from cable demotion; AOC/DAC keyword beats the optical-token guard. Consumers filter product_type=module. sql/120: conservative form_factor recovery. Corrects only provably-wrong rows (form_factors.max_speed < speed_gbps) via an explicit title token; 87 fixed (e.g. 400G cables mislabelled SFP+ to QSFP-DD), 947 opaque rows left untouched (never guessed). Scraped original preserved in new column form_factor_raw. sql/121: document lead_time_days as not-collected (0 percent populated on all time-series tables) so no consumer fabricates a default. scraper/utils/db.ts: note that product_type/form_factor are trigger-maintained.
125 lines
6.3 KiB
PL/PgSQL
125 lines
6.3 KiB
PL/PgSQL
-- Migration 120: conservative form_factor recovery (provably-wrong rows only)
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|
-- Problem: some scrapers default form_factor to 'SFP'/'SFP+', so a 400G QSFP-DD
|
|
-- DAC ends up tagged 'SFP+'. We only ever correct a form_factor that is
|
|
-- PROVABLY wrong — i.e. its max electrical speed (form_factors.max_speed_gbps)
|
|
-- is below the row's actual speed_gbps — and only when the part_number carries
|
|
-- an explicit form-factor token that CAN carry that speed. Opaque part numbers
|
|
-- (e.g. 'EOLS-1303-40-D') are left untouched; we never guess a form factor.
|
|
--
|
|
-- The scraped original is preserved in form_factor_raw for audit/rollback.
|
|
-- Ingest-time self-healing is added to the tip_transceivers_biu() trigger.
|
|
-- Idempotent.
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
-- 1. Preserve the scraped original ---------------------------------------------
|
|
ALTER TABLE transceivers ADD COLUMN IF NOT EXISTS form_factor_raw TEXT;
|
|
COMMENT ON COLUMN transceivers.form_factor_raw IS
|
|
'Form factor exactly as scraped, before tip_recover_form_factor() correction. Audit/rollback only.';
|
|
|
|
UPDATE transceivers SET form_factor_raw = form_factor
|
|
WHERE form_factor_raw IS NULL;
|
|
|
|
-- 2. Explicit form-factor token extraction (most-specific first) ----------------
|
|
CREATE OR REPLACE FUNCTION tip_extract_ff_token(p_part_number TEXT) RETURNS TEXT
|
|
LANGUAGE sql IMMUTABLE PARALLEL SAFE AS $$
|
|
SELECT CASE
|
|
WHEN p_part_number ~* 'qsfp-?dd-?800|qdd-?800' THEN 'QSFP-DD800'
|
|
-- Q56DD / QSFP56-DD is the 400G double-density QSFP connector (Dell naming) → QSFP-DD
|
|
WHEN p_part_number ~* 'q56dd|qsfp56-?dd' THEN 'QSFP-DD'
|
|
WHEN p_part_number ~* 'osfp-?xd' THEN 'OSFP-XD'
|
|
WHEN p_part_number ~* 'osfp224' THEN 'OSFP224'
|
|
WHEN p_part_number ~* 'osfp112' THEN 'OSFP112'
|
|
WHEN p_part_number ~* '\mosfp\M' THEN 'OSFP'
|
|
WHEN p_part_number ~* 'qsfp112' THEN 'QSFP112'
|
|
WHEN p_part_number ~* 'qsfp-?dd|\mqdd\M|qsfpdd' THEN 'QSFP-DD'
|
|
WHEN p_part_number ~* 'qsfp56|\mq56\M' THEN 'QSFP56'
|
|
WHEN p_part_number ~* 'qsfp28|\mq28\M' THEN 'QSFP28'
|
|
WHEN p_part_number ~* 'qsfp\+|qsfp-?plus' THEN 'QSFP+'
|
|
WHEN p_part_number ~* 'sfp-?dd|sfpdd' THEN 'SFP-DD'
|
|
WHEN p_part_number ~* 'sfp112' THEN 'SFP112'
|
|
WHEN p_part_number ~* 'sfp56' THEN 'SFP56'
|
|
WHEN p_part_number ~* 'sfp28' THEN 'SFP28'
|
|
WHEN p_part_number ~* 'sfp\+|sfp-?plus' THEN 'SFP+'
|
|
WHEN p_part_number ~* 'cfp2-?dco' THEN 'CFP2-DCO'
|
|
WHEN p_part_number ~* 'cfp2' THEN 'CFP2'
|
|
WHEN p_part_number ~* 'cfp8' THEN 'CFP8'
|
|
WHEN p_part_number ~* 'cfp4' THEN 'CFP4'
|
|
WHEN p_part_number ~* '\mcfp\M' THEN 'CFP'
|
|
WHEN p_part_number ~* '\mxfp\M' THEN 'XFP'
|
|
ELSE NULL
|
|
END;
|
|
$$;
|
|
|
|
-- 3. Recovery: return corrected FF only when the current one is PROVABLY wrong ---
|
|
CREATE OR REPLACE FUNCTION tip_recover_form_factor(
|
|
p_part_number TEXT, p_form_factor TEXT, p_speed_gbps NUMERIC
|
|
) RETURNS TEXT
|
|
LANGUAGE plpgsql STABLE PARALLEL SAFE AS $$
|
|
DECLARE
|
|
v_token TEXT := tip_extract_ff_token(p_part_number);
|
|
v_cur_max NUMERIC;
|
|
v_tok_max NUMERIC;
|
|
BEGIN
|
|
IF v_token IS NULL OR p_form_factor IS NULL OR p_speed_gbps IS NULL THEN
|
|
RETURN p_form_factor;
|
|
END IF;
|
|
IF upper(v_token) = upper(p_form_factor) THEN
|
|
RETURN p_form_factor; -- already correct
|
|
END IF;
|
|
SELECT max_speed_gbps INTO v_cur_max FROM form_factors WHERE upper(name) = upper(p_form_factor);
|
|
SELECT max_speed_gbps INTO v_tok_max FROM form_factors WHERE name = v_token;
|
|
-- current provably wrong for the speed, and the token can actually carry it
|
|
IF v_cur_max IS NOT NULL AND v_tok_max IS NOT NULL
|
|
AND p_speed_gbps > v_cur_max AND v_tok_max >= p_speed_gbps THEN
|
|
RETURN v_token;
|
|
END IF;
|
|
RETURN p_form_factor; -- not confident → leave as-is
|
|
END;
|
|
$$;
|
|
|
|
-- 4. Backfill the confidently-recoverable rows ---------------------------------
|
|
UPDATE transceivers
|
|
SET form_factor = tip_recover_form_factor(part_number, form_factor, speed_gbps)
|
|
WHERE form_factor IS DISTINCT FROM tip_recover_form_factor(part_number, form_factor, speed_gbps);
|
|
|
|
-- 5. Extend the ingest trigger with FF self-healing ----------------------------
|
|
CREATE OR REPLACE FUNCTION tip_transceivers_biu() RETURNS TRIGGER
|
|
LANGUAGE plpgsql AS $$
|
|
BEGIN
|
|
-- product_type: recompute only when a classification input changed
|
|
IF TG_OP = 'INSERT'
|
|
OR NEW.product_type IS NULL
|
|
OR NEW.part_number IS DISTINCT FROM OLD.part_number
|
|
OR NEW.category IS DISTINCT FROM OLD.category
|
|
OR NEW.reach_label IS DISTINCT FROM OLD.reach_label
|
|
THEN
|
|
NEW.product_type := tip_classify_product_type(NEW.part_number, NEW.category, NEW.reach_label);
|
|
END IF;
|
|
|
|
-- form_factor: preserve the scraped original, then correct only provably-wrong values
|
|
NEW.form_factor_raw := COALESCE(NEW.form_factor_raw, NEW.form_factor);
|
|
NEW.form_factor := tip_recover_form_factor(NEW.part_number, NEW.form_factor, NEW.speed_gbps);
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|
|
|
|
-- (trigger trg_transceivers_biu already bound in migration 119 — replacing the
|
|
-- function body above is sufficient.)
|
|
|
|
INSERT INTO _migrations (filename)
|
|
VALUES ('120-form-factor-recovery.sql')
|
|
ON CONFLICT (filename) DO NOTHING;
|
|
|
|
-- 6. Report --------------------------------------------------------------------
|
|
DO $$
|
|
DECLARE v_fixed INT; v_left INT;
|
|
BEGIN
|
|
SELECT count(*) INTO v_fixed FROM transceivers WHERE form_factor IS DISTINCT FROM form_factor_raw;
|
|
SELECT count(*) INTO v_left FROM transceivers t JOIN form_factors f ON upper(f.name)=upper(t.form_factor)
|
|
WHERE t.speed_gbps > f.max_speed_gbps;
|
|
RAISE NOTICE 'form_factor corrected on % rows; % provably-wrong rows left untouched (opaque, not guessed).', v_fixed, v_left;
|
|
END;
|
|
$$;
|