From 188040b8bb7ae1d0776e686504bae6945f8d55c2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 2 May 2022 17:17:21 -0500 Subject: [PATCH] Use lambda_stmt for recorder queries and migrate them to queries module (#71219) --- homeassistant/components/recorder/__init__.py | 47 +- homeassistant/components/recorder/purge.py | 455 +---------------- homeassistant/components/recorder/queries.py | 462 ++++++++++++++++++ tests/components/recorder/test_init.py | 2 - 4 files changed, 481 insertions(+), 485 deletions(-) create mode 100644 homeassistant/components/recorder/queries.py diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index 486866fc799..35a664cac65 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -14,19 +14,10 @@ import time from typing import Any, TypeVar, cast from lru import LRU # pylint: disable=no-name-in-module -from sqlalchemy import ( - bindparam, - create_engine, - event as sqlalchemy_event, - exc, - func, - select, -) +from sqlalchemy import create_engine, event as sqlalchemy_event, exc, func, select from sqlalchemy.engine import Engine from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy.ext import baked from sqlalchemy.orm import scoped_session, sessionmaker -from sqlalchemy.orm.query import Query from sqlalchemy.orm.session import Session import voluptuous as vol @@ -90,6 +81,7 @@ from .models import ( process_timestamp, ) from .pool import POOL_SIZE, MutexPool, RecorderPool +from .queries import find_shared_attributes_id, find_shared_data_id from .run_history import RunHistory from .util import ( dburl_to_path, @@ -292,7 +284,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: entity_filter=entity_filter, exclude_t=exclude_t, exclude_attributes_by_domain=exclude_attributes_by_domain, - bakery=baked.bakery(), ) instance.async_initialize() instance.async_register() @@ -614,7 +605,6 @@ class Recorder(threading.Thread): entity_filter: Callable[[str], bool], exclude_t: list[str], exclude_attributes_by_domain: dict[str, set[str]], - bakery: baked.bakery, ) -> None: """Initialize the recorder.""" threading.Thread.__init__(self, name="Recorder") @@ -645,9 +635,6 @@ class Recorder(threading.Thread): self._pending_state_attributes: dict[str, StateAttributes] = {} self._pending_event_data: dict[str, EventData] = {} self._pending_expunge: list[States] = [] - self._bakery = bakery - self._find_shared_attr_query: Query | None = None - self._find_shared_data_query: Query | None = None self.event_session: Session | None = None self.get_session: Callable[[], Session] | None = None self._completed_first_database_setup: bool | None = None @@ -1152,19 +1139,11 @@ class Recorder(threading.Thread): # need to flush before checking the database. # assert self.event_session is not None - if self._find_shared_attr_query is None: - self._find_shared_attr_query = self._bakery( - lambda session: session.query(StateAttributes.attributes_id) - .filter(StateAttributes.hash == bindparam("attr_hash")) - .filter(StateAttributes.shared_attrs == bindparam("shared_attrs")) - ) with self.event_session.no_autoflush: - if ( - attributes := self._find_shared_attr_query(self.event_session) - .params(attr_hash=attr_hash, shared_attrs=shared_attrs) - .first() - ): - return cast(int, attributes[0]) + if attributes_id := self.event_session.execute( + find_shared_attributes_id(attr_hash, shared_attrs) + ).first(): + return cast(int, attributes_id[0]) return None def _find_shared_data_in_db(self, data_hash: int, shared_data: str) -> int | None: @@ -1178,18 +1157,10 @@ class Recorder(threading.Thread): # need to flush before checking the database. # assert self.event_session is not None - if self._find_shared_data_query is None: - self._find_shared_data_query = self._bakery( - lambda session: session.query(EventData.data_id) - .filter(EventData.hash == bindparam("data_hash")) - .filter(EventData.shared_data == bindparam("shared_data")) - ) with self.event_session.no_autoflush: - if ( - data_id := self._find_shared_data_query(self.event_session) - .params(data_hash=data_hash, shared_data=shared_data) - .first() - ): + if data_id := self.event_session.execute( + find_shared_data_id(data_hash, shared_data) + ).first(): return cast(int, data_id[0]) return None diff --git a/homeassistant/components/recorder/purge.py b/homeassistant/components/recorder/purge.py index dcc535c8177..3a0e2e6e141 100644 --- a/homeassistant/components/recorder/purge.py +++ b/homeassistant/components/recorder/purge.py @@ -7,11 +7,9 @@ from itertools import zip_longest import logging from typing import TYPE_CHECKING -from sqlalchemy import func, lambda_stmt, select, union_all +from sqlalchemy import func from sqlalchemy.orm.session import Session from sqlalchemy.sql.expression import distinct -from sqlalchemy.sql.lambdas import StatementLambdaElement -from sqlalchemy.sql.selectable import Select from homeassistant.const import EVENT_STATE_CHANGED @@ -25,6 +23,7 @@ from .models import ( StatisticsRuns, StatisticsShortTerm, ) +from .queries import attributes_ids_exist_in_states, data_ids_exist_in_events from .repack import repack_database from .util import retryable_database_job, session_scope @@ -131,223 +130,6 @@ def _select_event_state_attributes_ids_data_ids_to_purge( return event_ids, state_ids, attributes_ids, data_ids -def _state_attrs_exist(attr: int | None) -> Select: - """Check if a state attributes id exists in the states table.""" - return select(func.min(States.attributes_id)).where(States.attributes_id == attr) - - -def _generate_find_attr_lambda( - attr1: int, - attr2: int | None, - attr3: int | None, - attr4: int | None, - attr5: int | None, - attr6: int | None, - attr7: int | None, - attr8: int | None, - attr9: int | None, - attr10: int | None, - attr11: int | None, - attr12: int | None, - attr13: int | None, - attr14: int | None, - attr15: int | None, - attr16: int | None, - attr17: int | None, - attr18: int | None, - attr19: int | None, - attr20: int | None, - attr21: int | None, - attr22: int | None, - attr23: int | None, - attr24: int | None, - attr25: int | None, - attr26: int | None, - attr27: int | None, - attr28: int | None, - attr29: int | None, - attr30: int | None, - attr31: int | None, - attr32: int | None, - attr33: int | None, - attr34: int | None, - attr35: int | None, - attr36: int | None, - attr37: int | None, - attr38: int | None, - attr39: int | None, - attr40: int | None, - attr41: int | None, - attr42: int | None, - attr43: int | None, - attr44: int | None, - attr45: int | None, - attr46: int | None, - attr47: int | None, - attr48: int | None, - attr49: int | None, - attr50: int | None, - attr51: int | None, - attr52: int | None, - attr53: int | None, - attr54: int | None, - attr55: int | None, - attr56: int | None, - attr57: int | None, - attr58: int | None, - attr59: int | None, - attr60: int | None, - attr61: int | None, - attr62: int | None, - attr63: int | None, - attr64: int | None, - attr65: int | None, - attr66: int | None, - attr67: int | None, - attr68: int | None, - attr69: int | None, - attr70: int | None, - attr71: int | None, - attr72: int | None, - attr73: int | None, - attr74: int | None, - attr75: int | None, - attr76: int | None, - attr77: int | None, - attr78: int | None, - attr79: int | None, - attr80: int | None, - attr81: int | None, - attr82: int | None, - attr83: int | None, - attr84: int | None, - attr85: int | None, - attr86: int | None, - attr87: int | None, - attr88: int | None, - attr89: int | None, - attr90: int | None, - attr91: int | None, - attr92: int | None, - attr93: int | None, - attr94: int | None, - attr95: int | None, - attr96: int | None, - attr97: int | None, - attr98: int | None, - attr99: int | None, - attr100: int | None, -) -> StatementLambdaElement: - """Generate the find attributes select only once. - - https://docs.sqlalchemy.org/en/14/core/connections.html#quick-guidelines-for-lambdas - """ - return lambda_stmt( - lambda: union_all( - _state_attrs_exist(attr1), - _state_attrs_exist(attr2), - _state_attrs_exist(attr3), - _state_attrs_exist(attr4), - _state_attrs_exist(attr5), - _state_attrs_exist(attr6), - _state_attrs_exist(attr7), - _state_attrs_exist(attr8), - _state_attrs_exist(attr9), - _state_attrs_exist(attr10), - _state_attrs_exist(attr11), - _state_attrs_exist(attr12), - _state_attrs_exist(attr13), - _state_attrs_exist(attr14), - _state_attrs_exist(attr15), - _state_attrs_exist(attr16), - _state_attrs_exist(attr17), - _state_attrs_exist(attr18), - _state_attrs_exist(attr19), - _state_attrs_exist(attr20), - _state_attrs_exist(attr21), - _state_attrs_exist(attr22), - _state_attrs_exist(attr23), - _state_attrs_exist(attr24), - _state_attrs_exist(attr25), - _state_attrs_exist(attr26), - _state_attrs_exist(attr27), - _state_attrs_exist(attr28), - _state_attrs_exist(attr29), - _state_attrs_exist(attr30), - _state_attrs_exist(attr31), - _state_attrs_exist(attr32), - _state_attrs_exist(attr33), - _state_attrs_exist(attr34), - _state_attrs_exist(attr35), - _state_attrs_exist(attr36), - _state_attrs_exist(attr37), - _state_attrs_exist(attr38), - _state_attrs_exist(attr39), - _state_attrs_exist(attr40), - _state_attrs_exist(attr41), - _state_attrs_exist(attr42), - _state_attrs_exist(attr43), - _state_attrs_exist(attr44), - _state_attrs_exist(attr45), - _state_attrs_exist(attr46), - _state_attrs_exist(attr47), - _state_attrs_exist(attr48), - _state_attrs_exist(attr49), - _state_attrs_exist(attr50), - _state_attrs_exist(attr51), - _state_attrs_exist(attr52), - _state_attrs_exist(attr53), - _state_attrs_exist(attr54), - _state_attrs_exist(attr55), - _state_attrs_exist(attr56), - _state_attrs_exist(attr57), - _state_attrs_exist(attr58), - _state_attrs_exist(attr59), - _state_attrs_exist(attr60), - _state_attrs_exist(attr61), - _state_attrs_exist(attr62), - _state_attrs_exist(attr63), - _state_attrs_exist(attr64), - _state_attrs_exist(attr65), - _state_attrs_exist(attr66), - _state_attrs_exist(attr67), - _state_attrs_exist(attr68), - _state_attrs_exist(attr69), - _state_attrs_exist(attr70), - _state_attrs_exist(attr71), - _state_attrs_exist(attr72), - _state_attrs_exist(attr73), - _state_attrs_exist(attr74), - _state_attrs_exist(attr75), - _state_attrs_exist(attr76), - _state_attrs_exist(attr77), - _state_attrs_exist(attr78), - _state_attrs_exist(attr79), - _state_attrs_exist(attr80), - _state_attrs_exist(attr81), - _state_attrs_exist(attr82), - _state_attrs_exist(attr83), - _state_attrs_exist(attr84), - _state_attrs_exist(attr85), - _state_attrs_exist(attr86), - _state_attrs_exist(attr87), - _state_attrs_exist(attr88), - _state_attrs_exist(attr89), - _state_attrs_exist(attr90), - _state_attrs_exist(attr91), - _state_attrs_exist(attr92), - _state_attrs_exist(attr93), - _state_attrs_exist(attr94), - _state_attrs_exist(attr95), - _state_attrs_exist(attr96), - _state_attrs_exist(attr97), - _state_attrs_exist(attr98), - _state_attrs_exist(attr99), - _state_attrs_exist(attr100), - ) - ) - - def _select_unused_attributes_ids( session: Session, attributes_ids: set[int], using_sqlite: bool ) -> set[int]: @@ -402,11 +184,11 @@ def _select_unused_attributes_ids( groups = [iter(attributes_ids)] * 100 for attr_ids in zip_longest(*groups, fillvalue=None): seen_ids |= { - state[0] - for state in session.execute( - _generate_find_attr_lambda(*attr_ids) + attrs_id[0] + for attrs_id in session.execute( + attributes_ids_exist_in_states(*attr_ids) ).all() - if state[0] is not None + if attrs_id[0] is not None } to_remove = attributes_ids - seen_ids _LOGGER.debug( @@ -416,223 +198,6 @@ def _select_unused_attributes_ids( return to_remove -def _event_data_id_exist(data_id: int | None) -> Select: - """Check if a event data id exists in the events table.""" - return select(func.min(Events.data_id)).where(Events.data_id == data_id) - - -def _generate_find_data_id_lambda( - id1: int, - id2: int | None, - id3: int | None, - id4: int | None, - id5: int | None, - id6: int | None, - id7: int | None, - id8: int | None, - id9: int | None, - id10: int | None, - id11: int | None, - id12: int | None, - id13: int | None, - id14: int | None, - id15: int | None, - id16: int | None, - id17: int | None, - id18: int | None, - id19: int | None, - id20: int | None, - id21: int | None, - id22: int | None, - id23: int | None, - id24: int | None, - id25: int | None, - id26: int | None, - id27: int | None, - id28: int | None, - id29: int | None, - id30: int | None, - id31: int | None, - id32: int | None, - id33: int | None, - id34: int | None, - id35: int | None, - id36: int | None, - id37: int | None, - id38: int | None, - id39: int | None, - id40: int | None, - id41: int | None, - id42: int | None, - id43: int | None, - id44: int | None, - id45: int | None, - id46: int | None, - id47: int | None, - id48: int | None, - id49: int | None, - id50: int | None, - id51: int | None, - id52: int | None, - id53: int | None, - id54: int | None, - id55: int | None, - id56: int | None, - id57: int | None, - id58: int | None, - id59: int | None, - id60: int | None, - id61: int | None, - id62: int | None, - id63: int | None, - id64: int | None, - id65: int | None, - id66: int | None, - id67: int | None, - id68: int | None, - id69: int | None, - id70: int | None, - id71: int | None, - id72: int | None, - id73: int | None, - id74: int | None, - id75: int | None, - id76: int | None, - id77: int | None, - id78: int | None, - id79: int | None, - id80: int | None, - id81: int | None, - id82: int | None, - id83: int | None, - id84: int | None, - id85: int | None, - id86: int | None, - id87: int | None, - id88: int | None, - id89: int | None, - id90: int | None, - id91: int | None, - id92: int | None, - id93: int | None, - id94: int | None, - id95: int | None, - id96: int | None, - id97: int | None, - id98: int | None, - id99: int | None, - id100: int | None, -) -> StatementLambdaElement: - """Generate the find event data select only once. - - https://docs.sqlalchemy.org/en/14/core/connections.html#quick-guidelines-for-lambdas - """ - return lambda_stmt( - lambda: union_all( - _event_data_id_exist(id1), - _event_data_id_exist(id2), - _event_data_id_exist(id3), - _event_data_id_exist(id4), - _event_data_id_exist(id5), - _event_data_id_exist(id6), - _event_data_id_exist(id7), - _event_data_id_exist(id8), - _event_data_id_exist(id9), - _event_data_id_exist(id10), - _event_data_id_exist(id11), - _event_data_id_exist(id12), - _event_data_id_exist(id13), - _event_data_id_exist(id14), - _event_data_id_exist(id15), - _event_data_id_exist(id16), - _event_data_id_exist(id17), - _event_data_id_exist(id18), - _event_data_id_exist(id19), - _event_data_id_exist(id20), - _event_data_id_exist(id21), - _event_data_id_exist(id22), - _event_data_id_exist(id23), - _event_data_id_exist(id24), - _event_data_id_exist(id25), - _event_data_id_exist(id26), - _event_data_id_exist(id27), - _event_data_id_exist(id28), - _event_data_id_exist(id29), - _event_data_id_exist(id30), - _event_data_id_exist(id31), - _event_data_id_exist(id32), - _event_data_id_exist(id33), - _event_data_id_exist(id34), - _event_data_id_exist(id35), - _event_data_id_exist(id36), - _event_data_id_exist(id37), - _event_data_id_exist(id38), - _event_data_id_exist(id39), - _event_data_id_exist(id40), - _event_data_id_exist(id41), - _event_data_id_exist(id42), - _event_data_id_exist(id43), - _event_data_id_exist(id44), - _event_data_id_exist(id45), - _event_data_id_exist(id46), - _event_data_id_exist(id47), - _event_data_id_exist(id48), - _event_data_id_exist(id49), - _event_data_id_exist(id50), - _event_data_id_exist(id51), - _event_data_id_exist(id52), - _event_data_id_exist(id53), - _event_data_id_exist(id54), - _event_data_id_exist(id55), - _event_data_id_exist(id56), - _event_data_id_exist(id57), - _event_data_id_exist(id58), - _event_data_id_exist(id59), - _event_data_id_exist(id60), - _event_data_id_exist(id61), - _event_data_id_exist(id62), - _event_data_id_exist(id63), - _event_data_id_exist(id64), - _event_data_id_exist(id65), - _event_data_id_exist(id66), - _event_data_id_exist(id67), - _event_data_id_exist(id68), - _event_data_id_exist(id69), - _event_data_id_exist(id70), - _event_data_id_exist(id71), - _event_data_id_exist(id72), - _event_data_id_exist(id73), - _event_data_id_exist(id74), - _event_data_id_exist(id75), - _event_data_id_exist(id76), - _event_data_id_exist(id77), - _event_data_id_exist(id78), - _event_data_id_exist(id79), - _event_data_id_exist(id80), - _event_data_id_exist(id81), - _event_data_id_exist(id82), - _event_data_id_exist(id83), - _event_data_id_exist(id84), - _event_data_id_exist(id85), - _event_data_id_exist(id86), - _event_data_id_exist(id87), - _event_data_id_exist(id88), - _event_data_id_exist(id89), - _event_data_id_exist(id90), - _event_data_id_exist(id91), - _event_data_id_exist(id92), - _event_data_id_exist(id93), - _event_data_id_exist(id94), - _event_data_id_exist(id95), - _event_data_id_exist(id96), - _event_data_id_exist(id97), - _event_data_id_exist(id98), - _event_data_id_exist(id99), - _event_data_id_exist(id100), - ) - ) - - def _select_unused_event_data_ids( session: Session, data_ids: set[int], using_sqlite: bool ) -> set[int]: @@ -654,11 +219,11 @@ def _select_unused_event_data_ids( groups = [iter(data_ids)] * 100 for data_ids_group in zip_longest(*groups, fillvalue=None): seen_ids |= { - state[0] - for state in session.execute( - _generate_find_data_id_lambda(*data_ids_group) + data_id[0] + for data_id in session.execute( + data_ids_exist_in_events(*data_ids_group) ).all() - if state[0] is not None + if data_id[0] is not None } to_remove = data_ids - seen_ids _LOGGER.debug("Selected %s shared event data to remove", len(to_remove)) diff --git a/homeassistant/components/recorder/queries.py b/homeassistant/components/recorder/queries.py new file mode 100644 index 00000000000..ff4662e27b1 --- /dev/null +++ b/homeassistant/components/recorder/queries.py @@ -0,0 +1,462 @@ +"""Queries for the recorder.""" +from __future__ import annotations + +from sqlalchemy import func, lambda_stmt, select, union_all +from sqlalchemy.sql.lambdas import StatementLambdaElement +from sqlalchemy.sql.selectable import Select + +from .models import EventData, Events, StateAttributes, States + + +def find_shared_attributes_id( + data_hash: int, shared_attrs: str +) -> StatementLambdaElement: + """Find an attributes_id by hash and shared_attrs.""" + return lambda_stmt( + lambda: select(StateAttributes.attributes_id) + .filter(StateAttributes.hash == data_hash) + .filter(StateAttributes.shared_attrs == shared_attrs) + ) + + +def find_shared_data_id(attr_hash: int, shared_data: str) -> StatementLambdaElement: + """Find a data_id by hash and shared_data.""" + return lambda_stmt( + lambda: select(EventData.data_id) + .filter(EventData.hash == attr_hash) + .filter(EventData.shared_data == shared_data) + ) + + +def _state_attrs_exist(attr: int | None) -> Select: + """Check if a state attributes id exists in the states table.""" + return select(func.min(States.attributes_id)).where(States.attributes_id == attr) + + +def attributes_ids_exist_in_states( + attr1: int, + attr2: int | None, + attr3: int | None, + attr4: int | None, + attr5: int | None, + attr6: int | None, + attr7: int | None, + attr8: int | None, + attr9: int | None, + attr10: int | None, + attr11: int | None, + attr12: int | None, + attr13: int | None, + attr14: int | None, + attr15: int | None, + attr16: int | None, + attr17: int | None, + attr18: int | None, + attr19: int | None, + attr20: int | None, + attr21: int | None, + attr22: int | None, + attr23: int | None, + attr24: int | None, + attr25: int | None, + attr26: int | None, + attr27: int | None, + attr28: int | None, + attr29: int | None, + attr30: int | None, + attr31: int | None, + attr32: int | None, + attr33: int | None, + attr34: int | None, + attr35: int | None, + attr36: int | None, + attr37: int | None, + attr38: int | None, + attr39: int | None, + attr40: int | None, + attr41: int | None, + attr42: int | None, + attr43: int | None, + attr44: int | None, + attr45: int | None, + attr46: int | None, + attr47: int | None, + attr48: int | None, + attr49: int | None, + attr50: int | None, + attr51: int | None, + attr52: int | None, + attr53: int | None, + attr54: int | None, + attr55: int | None, + attr56: int | None, + attr57: int | None, + attr58: int | None, + attr59: int | None, + attr60: int | None, + attr61: int | None, + attr62: int | None, + attr63: int | None, + attr64: int | None, + attr65: int | None, + attr66: int | None, + attr67: int | None, + attr68: int | None, + attr69: int | None, + attr70: int | None, + attr71: int | None, + attr72: int | None, + attr73: int | None, + attr74: int | None, + attr75: int | None, + attr76: int | None, + attr77: int | None, + attr78: int | None, + attr79: int | None, + attr80: int | None, + attr81: int | None, + attr82: int | None, + attr83: int | None, + attr84: int | None, + attr85: int | None, + attr86: int | None, + attr87: int | None, + attr88: int | None, + attr89: int | None, + attr90: int | None, + attr91: int | None, + attr92: int | None, + attr93: int | None, + attr94: int | None, + attr95: int | None, + attr96: int | None, + attr97: int | None, + attr98: int | None, + attr99: int | None, + attr100: int | None, +) -> StatementLambdaElement: + """Generate the find attributes select only once. + + https://docs.sqlalchemy.org/en/14/core/connections.html#quick-guidelines-for-lambdas + """ + return lambda_stmt( + lambda: union_all( + _state_attrs_exist(attr1), + _state_attrs_exist(attr2), + _state_attrs_exist(attr3), + _state_attrs_exist(attr4), + _state_attrs_exist(attr5), + _state_attrs_exist(attr6), + _state_attrs_exist(attr7), + _state_attrs_exist(attr8), + _state_attrs_exist(attr9), + _state_attrs_exist(attr10), + _state_attrs_exist(attr11), + _state_attrs_exist(attr12), + _state_attrs_exist(attr13), + _state_attrs_exist(attr14), + _state_attrs_exist(attr15), + _state_attrs_exist(attr16), + _state_attrs_exist(attr17), + _state_attrs_exist(attr18), + _state_attrs_exist(attr19), + _state_attrs_exist(attr20), + _state_attrs_exist(attr21), + _state_attrs_exist(attr22), + _state_attrs_exist(attr23), + _state_attrs_exist(attr24), + _state_attrs_exist(attr25), + _state_attrs_exist(attr26), + _state_attrs_exist(attr27), + _state_attrs_exist(attr28), + _state_attrs_exist(attr29), + _state_attrs_exist(attr30), + _state_attrs_exist(attr31), + _state_attrs_exist(attr32), + _state_attrs_exist(attr33), + _state_attrs_exist(attr34), + _state_attrs_exist(attr35), + _state_attrs_exist(attr36), + _state_attrs_exist(attr37), + _state_attrs_exist(attr38), + _state_attrs_exist(attr39), + _state_attrs_exist(attr40), + _state_attrs_exist(attr41), + _state_attrs_exist(attr42), + _state_attrs_exist(attr43), + _state_attrs_exist(attr44), + _state_attrs_exist(attr45), + _state_attrs_exist(attr46), + _state_attrs_exist(attr47), + _state_attrs_exist(attr48), + _state_attrs_exist(attr49), + _state_attrs_exist(attr50), + _state_attrs_exist(attr51), + _state_attrs_exist(attr52), + _state_attrs_exist(attr53), + _state_attrs_exist(attr54), + _state_attrs_exist(attr55), + _state_attrs_exist(attr56), + _state_attrs_exist(attr57), + _state_attrs_exist(attr58), + _state_attrs_exist(attr59), + _state_attrs_exist(attr60), + _state_attrs_exist(attr61), + _state_attrs_exist(attr62), + _state_attrs_exist(attr63), + _state_attrs_exist(attr64), + _state_attrs_exist(attr65), + _state_attrs_exist(attr66), + _state_attrs_exist(attr67), + _state_attrs_exist(attr68), + _state_attrs_exist(attr69), + _state_attrs_exist(attr70), + _state_attrs_exist(attr71), + _state_attrs_exist(attr72), + _state_attrs_exist(attr73), + _state_attrs_exist(attr74), + _state_attrs_exist(attr75), + _state_attrs_exist(attr76), + _state_attrs_exist(attr77), + _state_attrs_exist(attr78), + _state_attrs_exist(attr79), + _state_attrs_exist(attr80), + _state_attrs_exist(attr81), + _state_attrs_exist(attr82), + _state_attrs_exist(attr83), + _state_attrs_exist(attr84), + _state_attrs_exist(attr85), + _state_attrs_exist(attr86), + _state_attrs_exist(attr87), + _state_attrs_exist(attr88), + _state_attrs_exist(attr89), + _state_attrs_exist(attr90), + _state_attrs_exist(attr91), + _state_attrs_exist(attr92), + _state_attrs_exist(attr93), + _state_attrs_exist(attr94), + _state_attrs_exist(attr95), + _state_attrs_exist(attr96), + _state_attrs_exist(attr97), + _state_attrs_exist(attr98), + _state_attrs_exist(attr99), + _state_attrs_exist(attr100), + ) + ) + + +def _event_data_id_exist(data_id: int | None) -> Select: + """Check if a event data id exists in the events table.""" + return select(func.min(Events.data_id)).where(Events.data_id == data_id) + + +def data_ids_exist_in_events( + id1: int, + id2: int | None, + id3: int | None, + id4: int | None, + id5: int | None, + id6: int | None, + id7: int | None, + id8: int | None, + id9: int | None, + id10: int | None, + id11: int | None, + id12: int | None, + id13: int | None, + id14: int | None, + id15: int | None, + id16: int | None, + id17: int | None, + id18: int | None, + id19: int | None, + id20: int | None, + id21: int | None, + id22: int | None, + id23: int | None, + id24: int | None, + id25: int | None, + id26: int | None, + id27: int | None, + id28: int | None, + id29: int | None, + id30: int | None, + id31: int | None, + id32: int | None, + id33: int | None, + id34: int | None, + id35: int | None, + id36: int | None, + id37: int | None, + id38: int | None, + id39: int | None, + id40: int | None, + id41: int | None, + id42: int | None, + id43: int | None, + id44: int | None, + id45: int | None, + id46: int | None, + id47: int | None, + id48: int | None, + id49: int | None, + id50: int | None, + id51: int | None, + id52: int | None, + id53: int | None, + id54: int | None, + id55: int | None, + id56: int | None, + id57: int | None, + id58: int | None, + id59: int | None, + id60: int | None, + id61: int | None, + id62: int | None, + id63: int | None, + id64: int | None, + id65: int | None, + id66: int | None, + id67: int | None, + id68: int | None, + id69: int | None, + id70: int | None, + id71: int | None, + id72: int | None, + id73: int | None, + id74: int | None, + id75: int | None, + id76: int | None, + id77: int | None, + id78: int | None, + id79: int | None, + id80: int | None, + id81: int | None, + id82: int | None, + id83: int | None, + id84: int | None, + id85: int | None, + id86: int | None, + id87: int | None, + id88: int | None, + id89: int | None, + id90: int | None, + id91: int | None, + id92: int | None, + id93: int | None, + id94: int | None, + id95: int | None, + id96: int | None, + id97: int | None, + id98: int | None, + id99: int | None, + id100: int | None, +) -> StatementLambdaElement: + """Generate the find event data select only once. + + https://docs.sqlalchemy.org/en/14/core/connections.html#quick-guidelines-for-lambdas + """ + return lambda_stmt( + lambda: union_all( + _event_data_id_exist(id1), + _event_data_id_exist(id2), + _event_data_id_exist(id3), + _event_data_id_exist(id4), + _event_data_id_exist(id5), + _event_data_id_exist(id6), + _event_data_id_exist(id7), + _event_data_id_exist(id8), + _event_data_id_exist(id9), + _event_data_id_exist(id10), + _event_data_id_exist(id11), + _event_data_id_exist(id12), + _event_data_id_exist(id13), + _event_data_id_exist(id14), + _event_data_id_exist(id15), + _event_data_id_exist(id16), + _event_data_id_exist(id17), + _event_data_id_exist(id18), + _event_data_id_exist(id19), + _event_data_id_exist(id20), + _event_data_id_exist(id21), + _event_data_id_exist(id22), + _event_data_id_exist(id23), + _event_data_id_exist(id24), + _event_data_id_exist(id25), + _event_data_id_exist(id26), + _event_data_id_exist(id27), + _event_data_id_exist(id28), + _event_data_id_exist(id29), + _event_data_id_exist(id30), + _event_data_id_exist(id31), + _event_data_id_exist(id32), + _event_data_id_exist(id33), + _event_data_id_exist(id34), + _event_data_id_exist(id35), + _event_data_id_exist(id36), + _event_data_id_exist(id37), + _event_data_id_exist(id38), + _event_data_id_exist(id39), + _event_data_id_exist(id40), + _event_data_id_exist(id41), + _event_data_id_exist(id42), + _event_data_id_exist(id43), + _event_data_id_exist(id44), + _event_data_id_exist(id45), + _event_data_id_exist(id46), + _event_data_id_exist(id47), + _event_data_id_exist(id48), + _event_data_id_exist(id49), + _event_data_id_exist(id50), + _event_data_id_exist(id51), + _event_data_id_exist(id52), + _event_data_id_exist(id53), + _event_data_id_exist(id54), + _event_data_id_exist(id55), + _event_data_id_exist(id56), + _event_data_id_exist(id57), + _event_data_id_exist(id58), + _event_data_id_exist(id59), + _event_data_id_exist(id60), + _event_data_id_exist(id61), + _event_data_id_exist(id62), + _event_data_id_exist(id63), + _event_data_id_exist(id64), + _event_data_id_exist(id65), + _event_data_id_exist(id66), + _event_data_id_exist(id67), + _event_data_id_exist(id68), + _event_data_id_exist(id69), + _event_data_id_exist(id70), + _event_data_id_exist(id71), + _event_data_id_exist(id72), + _event_data_id_exist(id73), + _event_data_id_exist(id74), + _event_data_id_exist(id75), + _event_data_id_exist(id76), + _event_data_id_exist(id77), + _event_data_id_exist(id78), + _event_data_id_exist(id79), + _event_data_id_exist(id80), + _event_data_id_exist(id81), + _event_data_id_exist(id82), + _event_data_id_exist(id83), + _event_data_id_exist(id84), + _event_data_id_exist(id85), + _event_data_id_exist(id86), + _event_data_id_exist(id87), + _event_data_id_exist(id88), + _event_data_id_exist(id89), + _event_data_id_exist(id90), + _event_data_id_exist(id91), + _event_data_id_exist(id92), + _event_data_id_exist(id93), + _event_data_id_exist(id94), + _event_data_id_exist(id95), + _event_data_id_exist(id96), + _event_data_id_exist(id97), + _event_data_id_exist(id98), + _event_data_id_exist(id99), + _event_data_id_exist(id100), + ) + ) diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index 423e8eb2f48..0b03283547e 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -11,7 +11,6 @@ from unittest.mock import Mock, patch import pytest from sqlalchemy.exc import DatabaseError, OperationalError, SQLAlchemyError -from sqlalchemy.ext import baked from homeassistant.components import recorder from homeassistant.components.recorder import ( @@ -82,7 +81,6 @@ def _default_recorder(hass): entity_filter=CONFIG_SCHEMA({DOMAIN: {}}), exclude_t=[], exclude_attributes_by_domain={}, - bakery=baked.bakery(), )