Guard against selecting all invalid entity_ids in history (#89929)

If all the entity_ids that were provided do not exist we would
end up passing an empty list of ids to the SQL query which
would do an unbounded select
This commit is contained in:
J. Nick Koston 2023-03-19 16:03:12 -10:00 committed by GitHub
parent 5ffb233004
commit 7f3e4cb3af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 33 additions and 25 deletions

View File

@ -14,6 +14,7 @@ from homeassistant.components.recorder import get_instance
from homeassistant.components.recorder.filters import Filters
from homeassistant.components.recorder.models import (
bytes_to_uuid_hex_or_none,
extract_metadata_ids,
process_datetime_to_timestamp,
process_timestamp_to_utc_isoformat,
)
@ -154,14 +155,11 @@ class EventProcessor:
metadata_ids: list[int] | None = None
if self.entity_ids:
instance = get_instance(self.hass)
entity_id_to_metadata_id = instance.states_meta_manager.get_many(
self.entity_ids, session, False
metadata_ids = extract_metadata_ids(
instance.states_meta_manager.get_many(
self.entity_ids, session, False
)
)
metadata_ids = [
metadata_id
for metadata_id in entity_id_to_metadata_id.values()
if metadata_id is not None
]
stmt = statement_for_request(
start_day,
end_day,

View File

@ -23,7 +23,12 @@ import homeassistant.util.dt as dt_util
from ... import recorder
from ..db_schema import RecorderRuns, StateAttributes, States, StatesMeta
from ..filters import Filters
from ..models import LazyState, process_timestamp, row_to_compressed_state
from ..models import (
LazyState,
extract_metadata_ids,
process_timestamp,
row_to_compressed_state,
)
from ..util import execute_stmt_lambda_element, session_scope
from .const import (
IGNORE_DOMAINS_ENTITY_ID_LIKE,
@ -232,14 +237,12 @@ def get_significant_states_with_session(
entity_id_to_metadata_id: dict[str, int | None] | None = None
if entity_ids:
instance = recorder.get_instance(hass)
entity_id_to_metadata_id = instance.states_meta_manager.get_many(
entity_ids, session, False
)
metadata_ids = [
metadata_id
for metadata_id in entity_id_to_metadata_id.values()
if metadata_id is not None
]
if not (
entity_id_to_metadata_id := instance.states_meta_manager.get_many(
entity_ids, session, False
)
) or not (metadata_ids := extract_metadata_ids(entity_id_to_metadata_id)):
return {}
stmt = _significant_states_stmt(
start_time,
end_time,
@ -569,14 +572,9 @@ def _get_rows_with_session(
# We have more than one entity to look at so we need to do a query on states
# since the last recorder run started.
if entity_ids:
if not entity_id_to_metadata_id:
return []
metadata_ids = [
metadata_id
for metadata_id in entity_id_to_metadata_id.values()
if metadata_id is not None
]
if not metadata_ids:
if not entity_id_to_metadata_id or not (
metadata_ids := extract_metadata_ids(entity_id_to_metadata_id)
):
return []
stmt = _get_states_for_entities_stmt(
run.start, utc_point_in_time, metadata_ids, no_attributes

View File

@ -8,7 +8,7 @@ from .context import (
uuid_hex_to_bytes_or_none,
)
from .database import DatabaseEngine, DatabaseOptimizer, UnsupportedDialect
from .state import LazyState, row_to_compressed_state
from .state import LazyState, extract_metadata_ids, row_to_compressed_state
from .statistics import (
CalendarStatisticPeriod,
FixedStatisticPeriod,
@ -43,6 +43,7 @@ __all__ = [
"bytes_to_ulid_or_none",
"bytes_to_uuid_hex_or_none",
"datetime_to_timestamp_or_none",
"extract_metadata_ids",
"process_datetime_to_timestamp",
"process_timestamp",
"process_timestamp_to_utc_isoformat",

View File

@ -24,6 +24,17 @@ from .time import process_timestamp
_LOGGER = logging.getLogger(__name__)
def extract_metadata_ids(
entity_id_to_metadata_id: dict[str, int | None],
) -> list[int]:
"""Extract metadata ids from entity_id_to_metadata_id."""
return [
metadata_id
for metadata_id in entity_id_to_metadata_id.values()
if metadata_id is not None
]
class LazyState(State):
"""A lazy version of core State after schema 31."""