Upgrade SQLAlchemy to 2.0.2 (#86436)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Erik Montnemery 2023-02-08 15:17:32 +01:00 committed by GitHub
parent 93dafefd96
commit 94519de8dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 583 additions and 430 deletions

View file

@ -5,8 +5,7 @@ from typing import Final
import sqlalchemy
from sqlalchemy import select
from sqlalchemy.orm import Query
from sqlalchemy.sql.elements import ClauseList
from sqlalchemy.sql.elements import BooleanClauseList, ColumnElement
from sqlalchemy.sql.expression import literal
from sqlalchemy.sql.selectable import Select
@ -69,7 +68,7 @@ STATE_CONTEXT_ONLY_COLUMNS = (
literal(value=None, type_=sqlalchemy.String).label("old_format_icon"),
)
EVENT_COLUMNS_FOR_STATE_SELECT = [
EVENT_COLUMNS_FOR_STATE_SELECT = (
literal(value=None, type_=sqlalchemy.Text).label("event_id"),
# We use PSEUDO_EVENT_STATE_CHANGED aka None for
# state_changed events since it takes up less
@ -84,7 +83,7 @@ EVENT_COLUMNS_FOR_STATE_SELECT = [
States.context_user_id.label("context_user_id"),
States.context_parent_id.label("context_parent_id"),
literal(value=None, type_=sqlalchemy.Text).label("shared_data"),
]
)
EMPTY_STATE_COLUMNS = (
literal(value=0, type_=sqlalchemy.Integer).label("state_id"),
@ -103,8 +102,8 @@ EVENT_ROWS_NO_STATES = (
# Virtual column to tell logbook if it should avoid processing
# the event as its only used to link contexts
CONTEXT_ONLY = literal("1").label("context_only")
NOT_CONTEXT_ONLY = literal(None).label("context_only")
CONTEXT_ONLY = literal(value="1", type_=sqlalchemy.String).label("context_only")
NOT_CONTEXT_ONLY = literal(value=None, type_=sqlalchemy.String).label("context_only")
def select_events_context_id_subquery(
@ -188,7 +187,7 @@ def legacy_select_events_context_id(
)
def apply_states_filters(query: Query, start_day: float, end_day: float) -> Query:
def apply_states_filters(sel: Select, start_day: float, end_day: float) -> Select:
"""Filter states by time range.
Filters states that do not have an old state or new state (added / removed)
@ -196,7 +195,7 @@ def apply_states_filters(query: Query, start_day: float, end_day: float) -> Quer
Filters states that do not have matching last_updated_ts and last_changed_ts.
"""
return (
query.filter(
sel.filter(
(States.last_updated_ts > start_day) & (States.last_updated_ts < end_day)
)
.outerjoin(OLD_STATE, (States.old_state_id == OLD_STATE.state_id))
@ -212,18 +211,18 @@ def apply_states_filters(query: Query, start_day: float, end_day: float) -> Quer
)
def _missing_state_matcher() -> sqlalchemy.and_:
def _missing_state_matcher() -> ColumnElement[bool]:
# The below removes state change events that do not have
# and old_state or the old_state is missing (newly added entities)
# or the new_state is missing (removed entities)
return sqlalchemy.and_(
OLD_STATE.state_id.isnot(None),
OLD_STATE.state_id.is_not(None),
(States.state != OLD_STATE.state),
States.state.isnot(None),
States.state.is_not(None),
)
def _not_continuous_entity_matcher() -> sqlalchemy.or_:
def _not_continuous_entity_matcher() -> ColumnElement[bool]:
"""Match non continuous entities."""
return sqlalchemy.or_(
# First exclude domains that may be continuous
@ -236,7 +235,7 @@ def _not_continuous_entity_matcher() -> sqlalchemy.or_:
)
def _not_possible_continuous_domain_matcher() -> sqlalchemy.and_:
def _not_possible_continuous_domain_matcher() -> ColumnElement[bool]:
"""Match not continuous domains.
This matches domain that are always considered continuous
@ -254,7 +253,7 @@ def _not_possible_continuous_domain_matcher() -> sqlalchemy.and_:
).self_group()
def _conditionally_continuous_domain_matcher() -> sqlalchemy.or_:
def _conditionally_continuous_domain_matcher() -> ColumnElement[bool]:
"""Match conditionally continuous domains.
This matches domain that are only considered
@ -268,22 +267,22 @@ def _conditionally_continuous_domain_matcher() -> sqlalchemy.or_:
).self_group()
def _not_uom_attributes_matcher() -> ClauseList:
def _not_uom_attributes_matcher() -> BooleanClauseList:
"""Prefilter ATTR_UNIT_OF_MEASUREMENT as its much faster in sql."""
return ~StateAttributes.shared_attrs.like(
UNIT_OF_MEASUREMENT_JSON_LIKE
) | ~States.attributes.like(UNIT_OF_MEASUREMENT_JSON_LIKE)
def apply_states_context_hints(query: Query) -> Query:
def apply_states_context_hints(sel: Select) -> Select:
"""Force mysql to use the right index on large context_id selects."""
return query.with_hint(
return sel.with_hint(
States, f"FORCE INDEX ({STATES_CONTEXT_ID_INDEX})", dialect_name="mysql"
)
def apply_events_context_hints(query: Query) -> Query:
def apply_events_context_hints(sel: Select) -> Select:
"""Force mysql to use the right index on large context_id selects."""
return query.with_hint(
return sel.with_hint(
Events, f"FORCE INDEX ({EVENTS_CONTEXT_ID_INDEX})", dialect_name="mysql"
)