From 38e5af5373f01e90f8e053a360c743eaebe34a80 Mon Sep 17 00:00:00 2001 From: Dusan Vojacek Date: Sun, 5 Apr 2026 03:10:01 +0200 Subject: [PATCH] dalsi fixly --- ...032__market_interval_price_primary_key.sql | 89 ++++++++++++++++++- ...033__telemetry_hypertable_primary_keys.sql | 9 ++ scripts/import_ems_db.sh | 2 + 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/db/migration/V032__market_interval_price_primary_key.sql b/db/migration/V032__market_interval_price_primary_key.sql index 021ac5b..192a26b 100644 --- a/db/migration/V032__market_interval_price_primary_key.sql +++ b/db/migration/V032__market_interval_price_primary_key.sql @@ -1,7 +1,88 @@ -- OTE import (fn_ote_import_from_json) používá ON CONFLICT (market_source, interval_start). --- Bez odpovídajícího UNIQUE/PK PostgreSQL hlásí: --- "there is no unique or exclusion constraint matching the ON CONFLICT specification" --- Některé instalace (ruční DB, nestandardní pořadí migrací, restore) mohly zůstat bez PK. +-- Bez PK PostgreSQL hlásí chybu o ON CONFLICT. +-- +-- Na TimescaleDB hypertable může ALTER TABLE … ADD PRIMARY KEY selhat s: +-- ERROR: chunk not found … _timescaledb_internal._hyper_*_chunk +-- když v _timescaledb_catalog.chunk zůstaly řádky pro child tabulky, které fyzicky neexistují +-- (časté po pg_restore s paralelizací -j nebo neúplné obnově; viz timescale/timescaledb#4101). + +CREATE OR REPLACE FUNCTION ems._repair_ts_orphan_chunk_rows( + p_namespace name, + p_rel name +) +RETURNS integer +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path = pg_catalog, public +AS $$ +DECLARE + hid integer; + chunk_ids integer[]; + n integer; +BEGIN + SELECT h.id + INTO hid + FROM _timescaledb_catalog.hypertable h + WHERE h.schema_name = p_namespace + AND h.table_name = p_rel; + + IF hid IS NULL THEN + RETURN 0; + END IF; + + IF EXISTS ( + SELECT 1 + FROM pg_proc p + JOIN pg_namespace n ON n.oid = p.pronamespace + WHERE n.nspname = '_timescaledb_functions' + AND p.proname = 'remove_dropped_chunk_metadata' + ) THEN + PERFORM _timescaledb_functions.remove_dropped_chunk_metadata(hid); + END IF; + + SELECT coalesce(array_agg(c.id ORDER BY c.id), ARRAY[]::integer[]) + INTO chunk_ids + FROM _timescaledb_catalog.chunk c + WHERE c.hypertable_id = hid + AND NOT c.dropped + AND NOT EXISTS ( + SELECT 1 + FROM pg_catalog.pg_class cl + JOIN pg_catalog.pg_namespace ns ON ns.oid = cl.relnamespace + WHERE ns.nspname = c.schema_name + AND cl.relname = c.table_name + ); + + n := coalesce(array_length(chunk_ids, 1), 0); + IF n = 0 THEN + RETURN 0; + END IF; + + UPDATE _timescaledb_catalog.chunk ch + SET compressed_chunk_id = NULL + WHERE ch.compressed_chunk_id = ANY (chunk_ids); + + DELETE FROM _timescaledb_catalog.compression_chunk_size cs + WHERE cs.chunk_id = ANY (chunk_ids) + OR cs.compressed_chunk_id = ANY (chunk_ids); + + DELETE FROM _timescaledb_catalog.chunk_constraint cc + WHERE cc.chunk_id = ANY (chunk_ids); + + DELETE FROM _timescaledb_catalog.chunk_column_stats ccs + WHERE ccs.chunk_id = ANY (chunk_ids); + + DELETE FROM _timescaledb_internal.bgw_policy_chunk_stats b + WHERE b.chunk_id = ANY (chunk_ids); + + DELETE FROM _timescaledb_catalog.chunk c + WHERE c.id = ANY (chunk_ids); + + RETURN n; +END; +$$; + +SELECT ems._repair_ts_orphan_chunk_rows('ems', 'market_interval_price'); DO $$ BEGIN @@ -25,3 +106,5 @@ BEGIN ADD CONSTRAINT market_interval_price_pkey PRIMARY KEY (market_source, interval_start); END IF; END $$; + +-- Funkci znovu použije V033 pro telemetrické hypertably; drop až tam. diff --git a/db/migration/V033__telemetry_hypertable_primary_keys.sql b/db/migration/V033__telemetry_hypertable_primary_keys.sql index 0fc187b..ed99b4d 100644 --- a/db/migration/V033__telemetry_hypertable_primary_keys.sql +++ b/db/migration/V033__telemetry_hypertable_primary_keys.sql @@ -1,5 +1,8 @@ -- telemetry_collector: INSERT … ON CONFLICT vyžaduje UNIQUE/PK odpovídající cíli konfliktu. -- Stejná třída problému jako u market_interval_price (V032): DB bez primárních klíčů z V001. +-- Před ADD PK na hypertable voláme opravu katalogu chunků z V032 (funkce vytvořená tam). + +SELECT ems._repair_ts_orphan_chunk_rows('ems', 'telemetry_inverter'); DO $$ BEGIN @@ -18,6 +21,8 @@ BEGIN END IF; END $$; +SELECT ems._repair_ts_orphan_chunk_rows('ems', 'telemetry_ev_charger'); + DO $$ BEGIN IF EXISTS ( @@ -35,6 +40,8 @@ BEGIN END IF; END $$; +SELECT ems._repair_ts_orphan_chunk_rows('ems', 'telemetry_heat_pump'); + DO $$ BEGIN IF EXISTS ( @@ -56,3 +63,5 @@ END $$; CREATE UNIQUE INDEX IF NOT EXISTS uidx_ev_session_charger_open ON ems.ev_session (charger_id) WHERE session_end IS NULL; + +DROP FUNCTION IF EXISTS ems._repair_ts_orphan_chunk_rows(name, name); diff --git a/scripts/import_ems_db.sh b/scripts/import_ems_db.sh index c0e6df4..6f9e1b4 100755 --- a/scripts/import_ems_db.sh +++ b/scripts/import_ems_db.sh @@ -18,6 +18,8 @@ # Poznámky: # - Záloha musí být z podobného stacku (Postgres 16 + Timescale). Jiná major verze může selhat. # - Dump by měl obsahovat i Flyway metadata (tabulku historie), jinak při dalším startu Flyway znovu spustí migrace a může spadnout. +# - TimescaleDB: u pg_restore nepoužívejte paralelizaci (-j); může zůstat nekonzistentní katalog chunků +# (chyba „chunk not found“ / selhání ADD PK na hypertable). Viz dokumentace Timescale backup/restore. set -euo pipefail