-> Refonte du système de login -> Récupération du MDP -> Refonte imports d'horaires + Modale de gestion d'horaires
329 lines
14 KiB
SQL
329 lines
14 KiB
SQL
-- ============================================================================
|
|
-- MIGRATION : OPTIMISATION DE LA TABLE UNIQUE "horaires"
|
|
-- Date : 2026-10-01
|
|
-- Objectif : Consolider 100% des données relatives aux horaires (sillons, dessertes,
|
|
-- quais, arrêts intermédiaires, substitutions, calendrier, matériel,
|
|
-- temps réel) dans une seule et unique table "horaires" ultra-performante.
|
|
-- ============================================================================
|
|
|
|
-- 1. Ajout de toutes les colonnes nécessaires à la table horaires
|
|
DO $$
|
|
BEGIN
|
|
-- Identifiants et informations train
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'nom_train') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "nom_train" varchar(255);
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'code_mission') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "code_mission" varchar(50);
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'sens') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "sens" varchar(20) DEFAULT 'aller';
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'transporteur') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "transporteur" varchar(100) DEFAULT 'SNCF Voyageurs';
|
|
END IF;
|
|
|
|
-- Dessertes et attribution des quais
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'gares_desservies') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "gares_desservies" jsonb NOT NULL DEFAULT '[]'::jsonb;
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'attributions_quais') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "attributions_quais" jsonb NOT NULL DEFAULT '{}'::jsonb;
|
|
END IF;
|
|
|
|
-- Calendrier étendu et dates de validité
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'date_debut') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "date_debut" timestamptz;
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'date_fin') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "date_fin" timestamptz;
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'calendrier_circulation') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "calendrier_circulation" jsonb NOT NULL DEFAULT '[]'::jsonb;
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'periode_validite') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "periode_validite" jsonb NOT NULL DEFAULT '{}'::jsonb;
|
|
END IF;
|
|
|
|
-- Matériel et composition
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'composition_train') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "composition_train" jsonb NOT NULL DEFAULT '[]'::jsonb;
|
|
END IF;
|
|
|
|
-- Région
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'region') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "region" varchar(255);
|
|
END IF;
|
|
|
|
-- Service annuel et type de service
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'service_type') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "service_type" varchar(50) DEFAULT 'TER';
|
|
END IF;
|
|
|
|
-- Substitutions et autocars
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'est_substitution') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "est_substitution" boolean NOT NULL DEFAULT false;
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'motif_substitution') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "motif_substitution" varchar(255);
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'substitution_disponible') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "substitution_disponible" boolean NOT NULL DEFAULT false;
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'horaire_id') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "horaire_id" uuid;
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'substitutions_associees') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "substitutions_associees" jsonb NOT NULL DEFAULT '[]'::jsonb;
|
|
END IF;
|
|
|
|
-- Sillons & infrastructure
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'sillon_id') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "sillon_id" text;
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'tranche') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "tranche" varchar(50);
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'gestionnaire_infrastructure') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "gestionnaire_infrastructure" varchar(100) DEFAULT 'SNCF Réseau';
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'vitesse_max_kmh') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "vitesse_max_kmh" integer;
|
|
END IF;
|
|
|
|
-- Exploitation temps réel et retards
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'statut') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "statut" varchar(50) NOT NULL DEFAULT 'a_l_heure';
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'retard_minutes') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "retard_minutes" integer NOT NULL DEFAULT 0;
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'retard_motif') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "retard_motif" text;
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'informations_voyageurs') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "informations_voyageurs" text;
|
|
END IF;
|
|
|
|
-- Métadonnées et notes
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'metadata') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "metadata" jsonb NOT NULL DEFAULT '{}'::jsonb;
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'horaires' AND column_name = 'notes') THEN
|
|
ALTER TABLE "public"."horaires" ADD COLUMN "notes" text;
|
|
END IF;
|
|
END $$;
|
|
|
|
-- 2. Migration des données relationnelles de horaires_gares_desservies vers horaires.gares_desservies (si la table existe)
|
|
DO $$
|
|
BEGIN
|
|
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'horaires_gares_desservies') THEN
|
|
-- Mettre à jour horaires.gares_desservies avec les lignes de la table relationnelle si gares_desservies est vide
|
|
UPDATE "public"."horaires" h
|
|
SET gares_desservies = sub.json_dessertes,
|
|
updated_at = NOW()
|
|
FROM (
|
|
SELECT
|
|
horaire_id,
|
|
jsonb_agg(
|
|
jsonb_build_object(
|
|
'gare_id', gare_id,
|
|
'gare_nom', gare_nom,
|
|
'heure_arrivee', heure_arrivee,
|
|
'heure_depart', heure_depart,
|
|
'ordre', ordre,
|
|
'quai', quai
|
|
) ORDER BY ordre ASC
|
|
) AS json_dessertes
|
|
FROM "public"."horaires_gares_desservies"
|
|
GROUP BY horaire_id
|
|
) sub
|
|
WHERE h.id = sub.horaire_id
|
|
AND (h.gares_desservies IS NULL OR h.gares_desservies = '[]'::jsonb);
|
|
END IF;
|
|
END $$;
|
|
|
|
-- 3. Migration des substitutions de horaires_substitutions vers horaires (si la table existe)
|
|
DO $$
|
|
BEGIN
|
|
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'horaires_substitutions') THEN
|
|
INSERT INTO "public"."horaires" (
|
|
"id",
|
|
"numero_train",
|
|
"type_train",
|
|
"type_train_id",
|
|
"gare_depart_id",
|
|
"gare_depart_nom",
|
|
"heure_depart",
|
|
"gare_arrivee_id",
|
|
"gare_arrivee_nom",
|
|
"heure_arrivee",
|
|
"date_debut",
|
|
"date_fin",
|
|
"circule_lundi",
|
|
"circule_mardi",
|
|
"circule_mercredi",
|
|
"circule_jeudi",
|
|
"circule_vendredi",
|
|
"circule_samedi",
|
|
"circule_dimanche",
|
|
"circule_jours_feries",
|
|
"jours_personnalises",
|
|
"jours_non_circulation",
|
|
"materiel_roulant_id",
|
|
"ligne_id",
|
|
"ligne_nom",
|
|
"service_annuel_id",
|
|
"service_annuel_nom",
|
|
"est_substitution",
|
|
"motif_substitution",
|
|
"gares_desservies",
|
|
"composition_train",
|
|
"substitution_disponible",
|
|
"actif",
|
|
"region_id",
|
|
"region",
|
|
"horaire_id",
|
|
"created_at",
|
|
"updated_at",
|
|
"deleted_at"
|
|
)
|
|
SELECT
|
|
s."id",
|
|
COALESCE(s."numero_train", 'SUBST-' || substring(s."id"::text, 1, 6)),
|
|
s."type_train",
|
|
s."type_train_id",
|
|
s."gare_depart_id",
|
|
s."gare_depart_nom",
|
|
s."heure_depart",
|
|
s."gare_arrivee_id",
|
|
s."gare_arrivee_nom",
|
|
s."heure_arrivee",
|
|
s."date_debut",
|
|
s."date_fin",
|
|
COALESCE(s."circule_lundi", true),
|
|
COALESCE(s."circule_mardi", true),
|
|
COALESCE(s."circule_mercredi", true),
|
|
COALESCE(s."circule_jeudi", true),
|
|
COALESCE(s."circule_vendredi", true),
|
|
COALESCE(s."circule_samedi", true),
|
|
COALESCE(s."circule_dimanche", true),
|
|
COALESCE(s."circule_jours_feries", false),
|
|
COALESCE(s."jours_personnalises", '[]'::jsonb),
|
|
COALESCE(s."jours_non_circulation", '[]'::jsonb),
|
|
s."materiel_roulant_id",
|
|
s."ligne_id",
|
|
s."ligne_nom",
|
|
s."service_annuel_id",
|
|
s."service_annuel_nom",
|
|
true AS est_substitution,
|
|
s."motif_substitution",
|
|
COALESCE(s."gares_desservies", '[]'::jsonb),
|
|
COALESCE(s."composition_train", '[]'::jsonb),
|
|
false,
|
|
COALESCE(s."actif", true),
|
|
s."region_id",
|
|
s."region",
|
|
s."horaire_id",
|
|
COALESCE(s."created_at", NOW()),
|
|
COALESCE(s."updated_at", NOW()),
|
|
s."deleted_at"
|
|
FROM "public"."horaires_substitutions" s
|
|
ON CONFLICT ("id") DO UPDATE
|
|
SET est_substitution = true,
|
|
motif_substitution = EXCLUDED.motif_substitution,
|
|
date_debut = EXCLUDED.date_debut,
|
|
date_fin = EXCLUDED.date_fin,
|
|
updated_at = NOW();
|
|
END IF;
|
|
END $$;
|
|
|
|
-- 4. Initialisation du dictionnaire attributions_quais depuis gares_desservies
|
|
UPDATE "public"."horaires"
|
|
SET attributions_quais = (
|
|
SELECT COALESCE(
|
|
jsonb_object_agg(
|
|
COALESCE(elem->>'gare_id', elem->>'gare_nom'),
|
|
jsonb_build_object(
|
|
'quai', elem->>'quai',
|
|
'quai_reel', elem->>'quai_reel',
|
|
'ordre', elem->'ordre'
|
|
)
|
|
),
|
|
'{}'::jsonb
|
|
)
|
|
FROM jsonb_array_elements(gares_desservies) elem
|
|
WHERE elem->>'quai' IS NOT NULL OR elem->>'quai_reel' IS NOT NULL
|
|
)
|
|
WHERE attributions_quais = '{}'::jsonb
|
|
AND gares_desservies IS NOT NULL
|
|
AND jsonb_array_length(gares_desservies) > 0;
|
|
|
|
-- 5. Création des index haute performance (B-Tree et GIN)
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_numero_train" ON "public"."horaires" ("numero_train");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_ligne_id" ON "public"."horaires" ("ligne_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_region_id" ON "public"."horaires" ("region_id");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_departs" ON "public"."horaires" ("gare_depart_id", "heure_depart");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_arrivees" ON "public"."horaires" ("gare_arrivee_id", "heure_arrivee");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_actif" ON "public"."horaires" ("actif") WHERE deleted_at IS NULL;
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_substitution" ON "public"."horaires" ("est_substitution");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_dates" ON "public"."horaires" ("date_debut", "date_fin");
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_statut" ON "public"."horaires" ("statut");
|
|
|
|
-- Index GIN JSONB pour recherche et filtrage ultra-rapide sans jointure
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_gares_desservies_gin"
|
|
ON "public"."horaires" USING gin ("gares_desservies" jsonb_path_ops);
|
|
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_attributions_quais_gin"
|
|
ON "public"."horaires" USING gin ("attributions_quais");
|
|
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_calendrier_gin"
|
|
ON "public"."horaires" USING gin ("calendrier_circulation" jsonb_path_ops);
|
|
|
|
CREATE INDEX IF NOT EXISTS "idx_horaires_metadata_gin"
|
|
ON "public"."horaires" USING gin ("metadata");
|
|
|
|
-- 6. Trigger de mise à jour automatique de updated_at
|
|
CREATE OR REPLACE TRIGGER "tr_horaires_set_updated_at"
|
|
BEFORE UPDATE ON "public"."horaires"
|
|
FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
|
|
|
|
-- 7. Politiques de sécurité RLS
|
|
ALTER TABLE "public"."horaires" ENABLE ROW LEVEL SECURITY;
|
|
|
|
DROP POLICY IF EXISTS "Public read access" ON "public"."horaires";
|
|
CREATE POLICY "Public read access" ON "public"."horaires" FOR SELECT USING (true);
|
|
|
|
DROP POLICY IF EXISTS "Public insert access" ON "public"."horaires";
|
|
CREATE POLICY "Public insert access" ON "public"."horaires" FOR INSERT WITH CHECK (true);
|
|
|
|
DROP POLICY IF EXISTS "Public update access" ON "public"."horaires";
|
|
CREATE POLICY "Public update access" ON "public"."horaires" FOR UPDATE USING (true);
|
|
|
|
DROP POLICY IF EXISTS "Public delete access" ON "public"."horaires";
|
|
CREATE POLICY "Public delete access" ON "public"."horaires" FOR DELETE USING (true);
|
|
|
|
-- Commentaires d'optimisation
|
|
COMMENT ON TABLE "public"."horaires" IS 'Table UNIQUE et centrale pour stocker l''intégralité des données horaires, dessertes, quais, substitutions, calendrier et temps réel.';
|
|
COMMENT ON COLUMN "public"."horaires"."gares_desservies" IS 'Liste JSONB complète et ordonnée de tous les arrêts, gares, horaires et quais.';
|
|
COMMENT ON COLUMN "public"."horaires"."attributions_quais" IS 'Dictionnaire JSONB rapide O(1) { [gare_id]: { quai, quai_reel } } sans jointure.';
|
|
COMMENT ON COLUMN "public"."horaires"."est_substitution" IS 'Vrai si ce sillon représente un autocar ou train de substitution.';
|