From b9ffd74db5d32dc5d35cabe42343583833dc5f83 Mon Sep 17 00:00:00 2001 From: Marcin Ciupak <32123526+mciupak@users.noreply.github.com> Date: Tue, 21 Sep 2021 00:38:42 +0200 Subject: [PATCH] Fix recorder Oracle DB models (#55564) * Fix recorder models for Oracle DB * Fix StatisticsRuns * Update migration for Oracle Identity columns. * Update migration logic Migration to schema version 22 done only for engine dialect oracle * Add missing table check in schema 22 migration --- .../components/recorder/migration.py | 32 ++++++++++++++++++- homeassistant/components/recorder/models.py | 8 ++--- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/recorder/migration.py b/homeassistant/components/recorder/migration.py index c7ccefeca02..3ceac8903a7 100644 --- a/homeassistant/components/recorder/migration.py +++ b/homeassistant/components/recorder/migration.py @@ -20,6 +20,7 @@ from .models import ( Statistics, StatisticsMeta, StatisticsRuns, + StatisticsShortTerm, ) from .statistics import get_start_time from .util import session_scope @@ -484,7 +485,7 @@ def _apply_update(engine, session, new_version, old_version): # noqa: C901 session.add(StatisticsRuns(start=get_start_time())) elif new_version == 20: # This changed the precision of statistics from float to double - if engine.dialect.name in ["mysql", "oracle", "postgresql"]: + if engine.dialect.name in ["mysql", "postgresql"]: _modify_columns( connection, engine, @@ -513,6 +514,35 @@ def _apply_update(engine, session, new_version, old_version): # noqa: C901 "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" ) ) + elif new_version == 22: + # Recreate the all statistics tables for Oracle DB with Identity columns + # + # Order matters! Statistics has a relation with StatisticsMeta, + # so statistics need to be deleted before meta (or in pair depending + # on the SQL backend); and meta needs to be created before statistics. + if engine.dialect.name == "oracle": + if ( + sqlalchemy.inspect(engine).has_table(StatisticsMeta.__tablename__) + or sqlalchemy.inspect(engine).has_table(Statistics.__tablename__) + or sqlalchemy.inspect(engine).has_table(StatisticsRuns.__tablename__) + or sqlalchemy.inspect(engine).has_table( + StatisticsShortTerm.__tablename__ + ) + ): + Base.metadata.drop_all( + bind=engine, + tables=[ + StatisticsShortTerm.__table__, + Statistics.__table__, + StatisticsMeta.__table__, + StatisticsRuns.__table__, + ], + ) + + StatisticsRuns.__table__.create(engine) + StatisticsMeta.__table__.create(engine) + StatisticsShortTerm.__table__.create(engine) + Statistics.__table__.create(engine) else: raise ValueError(f"No schema migration defined for version {new_version}") diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 1c5c9fa90f0..354811bee40 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -40,7 +40,7 @@ import homeassistant.util.dt as dt_util # pylint: disable=invalid-name Base = declarative_base() -SCHEMA_VERSION = 21 +SCHEMA_VERSION = 22 _LOGGER = logging.getLogger(__name__) @@ -238,7 +238,7 @@ class StatisticData(TypedDict, total=False): class StatisticsBase: """Statistics base class.""" - id = Column(Integer, primary_key=True) + id = Column(Integer, Identity(), primary_key=True) created = Column(DATETIME_TYPE, default=dt_util.utcnow) @declared_attr @@ -309,7 +309,7 @@ class StatisticsMeta(Base): # type: ignore {"mysql_default_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}, ) __tablename__ = TABLE_STATISTICS_META - id = Column(Integer, primary_key=True) + id = Column(Integer, Identity(), primary_key=True) statistic_id = Column(String(255), index=True) source = Column(String(32)) unit_of_measurement = Column(String(255)) @@ -406,7 +406,7 @@ class StatisticsRuns(Base): # type: ignore """Representation of statistics run.""" __tablename__ = TABLE_STATISTICS_RUNS - run_id = Column(Integer, primary_key=True) + run_id = Column(Integer, Identity(), primary_key=True) start = Column(DateTime(timezone=True)) def __repr__(self) -> str: