adsb-1090/sql/02_convert_partitions_vides.sql

99 lines
4.2 KiB
SQL

-- =============================================================================
-- 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