-- =============================================================================
-- 02_convert_partitions_vides.sql — supprime les partitions HASH VIDES et les
-- recrée en RANGE simple, avec leurs index.
--
-- Sans risque de perte : chaque partition est recomptée juste avant sa
-- suppression, et toute partition non vide est IGNORÉE (jamais supprimée).
-- C'est le garde-fou principal de ce script.
--
-- Gain attendu : ~2000 sous-partitions HASH en moins, donc un temps de
-- planification qui retombe de ~1500 ms à quelques millisecondes. L'exécution
-- des requêtes, elle, était déjà à ~5 ms — c'est bien la planification qui
-- coûte, car le planner doit ouvrir toutes les partitions pour les élaguer.
--
-- Usage OBLIGATOIRE avec ON_ERROR_STOP : sans ce flag, psql poursuit après une
-- erreur et pourrait exécuter un DROP alors que le contrôle a échoué.
--   docker exec -i wordpress-postgres psql -U pgz_admin -d wpz_postgres \
--     -v ON_ERROR_STOP=1 < 02_convert_partitions_vides.sql
-- =============================================================================

\timing on
\set ON_ERROR_STOP on

DO $$
DECLARE
  r          record;
  n          bigint;
  d0         date;
  d1         date;
  tbl        text;
  converties int := 0;
  ignorees   int := 0;
BEGIN
  FOR r IN
    SELECT c.oid, c.relname::text AS partition,
           pg_get_expr(c.relpartbound, c.oid) AS bornes
    FROM pg_inherits i JOIN pg_class c ON c.oid = i.inhrelid
    WHERE i.inhparent = 'adsb.positions'::regclass
      AND c.relkind = 'p'
    ORDER BY pg_get_expr(c.relpartbound, c.oid)
  LOOP
    -- GARDE-FOU : comptage réel immédiatement avant toute action destructive.
    EXECUTE format('SELECT count(*) FROM adsb.%I', r.partition) INTO n;

    IF n > 0 THEN
      RAISE NOTICE 'IGNORÉE % : % ligne(s) — migration avec copie requise (script 03)',
                   r.partition, n;
      ignorees := ignorees + 1;
      CONTINUE;
    END IF;

    -- Extraction des bornes réelles depuis relpartbound : on ne se fie pas au
    -- nom, qui peut être erroné (bug EXTRACT(YEAR) vs ISOYEAR).
    d0 := (regexp_match(r.bornes, 'FROM \(''([0-9-]+)'))[1]::date;
    d1 := (regexp_match(r.bornes, 'TO \(''([0-9-]+)'))[1]::date;
    tbl := r.partition;

    EXECUTE format('DROP TABLE adsb.%I', tbl);

    EXECUTE format(
      'CREATE TABLE adsb.%I PARTITION OF adsb.positions FOR VALUES FROM (%L) TO (%L)',
      tbl, d0, d1);

    -- Index identiques à ceux posés par adsb.create_week_partition
    EXECUTE format('CREATE INDEX %I ON adsb.%I USING GIST (geom)', tbl||'_geom_gix', tbl);
    EXECUTE format('CREATE INDEX %I ON adsb.%I (ts DESC)', tbl||'_ts_idx', tbl);
    EXECUTE format('CREATE INDEX %I ON adsb.%I (icao, ts DESC)', tbl||'_icao_ts_idx', tbl);
    EXECUTE format('CREATE INDEX %I ON adsb.%I (inserted_at DESC)', tbl||'_inserted_at_idx', tbl);
    EXECUTE format('CREATE INDEX %I ON adsb.%I (callsign) WHERE callsign IS NOT NULL',
                   tbl||'_callsign_idx', tbl);
    EXECUTE format('CREATE INDEX %I ON adsb.%I (snap_id)', tbl||'_snap_id_idx', tbl);
    EXECUTE format('CREATE INDEX %I ON adsb.%I (receiver_id)', tbl||'_receiver_id_idx', tbl);
    EXECUTE format('CREATE INDEX %I ON adsb.%I (aircraft_wtc)', tbl||'_aircraft_wtc_idx', tbl);
    EXECUTE format(
      'ALTER TABLE adsb.%I ADD CONSTRAINT %I FOREIGN KEY (receiver_id) '
      'REFERENCES adsb.receivers(id) ON DELETE SET NULL',
      tbl, tbl||'_receiver_fk');

    converties := converties + 1;
  END LOOP;

  RAISE NOTICE '─────────────────────────────────────────────';
  RAISE NOTICE '% partition(s) converties en RANGE simple', converties;
  RAISE NOTICE '% partition(s) ignorées (contiennent des données)', ignorees;
END $$;

-- Résultat
SELECT c.relkind, count(*) AS nb
FROM pg_inherits i JOIN pg_class c ON c.oid = i.inhrelid
WHERE i.inhparent = 'adsb.positions'::regclass
GROUP BY 1 ORDER BY 1;

SELECT count(*) AS total_partitions_et_sous_partitions
FROM pg_class c JOIN pg_inherits i ON i.inhrelid = c.oid
WHERE i.inhparent = 'adsb.positions'::regclass
   OR i.inhparent IN (SELECT inhrelid FROM pg_inherits
                      WHERE inhparent = 'adsb.positions'::regclass);

\timing off
