mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Bump SQLAlchemy to 2.0.36 (#126683)
* Bump SQLAlchemy to 2.0.35 changelog: https://docs.sqlalchemy.org/en/20/changelog/changelog_20.html#change-2.0.35 * fix mocking * adjust to .36 * remove ignored as these are now typed * fix SQLAlchemy
This commit is contained in:
parent
44ed83a829
commit
ffeefd4856
2
.github/workflows/wheels.yml
vendored
2
.github/workflows/wheels.yml
vendored
@ -143,7 +143,7 @@ jobs:
|
||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||
env-file: true
|
||||
apk: "libffi-dev;openssl-dev;yaml-dev;nasm;zlib-dev"
|
||||
skip-binary: aiohttp;multidict;yarl
|
||||
skip-binary: aiohttp;multidict;yarl;SQLAlchemy
|
||||
constraints: "homeassistant/package_constraints.txt"
|
||||
requirements-diff: "requirements_diff.txt"
|
||||
requirements: "requirements.txt"
|
||||
|
@ -162,14 +162,14 @@ class Unused(CHAR):
|
||||
"""An unused column type that behaves like a string."""
|
||||
|
||||
|
||||
@compiles(UnusedDateTime, "mysql", "mariadb", "sqlite") # type: ignore[misc,no-untyped-call]
|
||||
@compiles(Unused, "mysql", "mariadb", "sqlite") # type: ignore[misc,no-untyped-call]
|
||||
@compiles(UnusedDateTime, "mysql", "mariadb", "sqlite")
|
||||
@compiles(Unused, "mysql", "mariadb", "sqlite")
|
||||
def compile_char_zero(type_: TypeDecorator, compiler: Any, **kw: Any) -> str:
|
||||
"""Compile UnusedDateTime and Unused as CHAR(0) on mysql, mariadb, and sqlite."""
|
||||
return "CHAR(0)" # Uses 1 byte on MySQL (no change on sqlite)
|
||||
|
||||
|
||||
@compiles(Unused, "postgresql") # type: ignore[misc,no-untyped-call]
|
||||
@compiles(Unused, "postgresql")
|
||||
def compile_char_one(type_: TypeDecorator, compiler: Any, **kw: Any) -> str:
|
||||
"""Compile Unused as CHAR(1) on postgresql."""
|
||||
return "CHAR(1)" # Uses 1 byte
|
||||
|
@ -7,7 +7,7 @@
|
||||
"iot_class": "local_push",
|
||||
"quality_scale": "internal",
|
||||
"requirements": [
|
||||
"SQLAlchemy==2.0.31",
|
||||
"SQLAlchemy==2.0.36",
|
||||
"fnv-hash-fast==1.0.2",
|
||||
"psutil-home-assistant==0.0.1"
|
||||
]
|
||||
|
@ -5,5 +5,5 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/sql",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["SQLAlchemy==2.0.31", "sqlparse==0.5.0"]
|
||||
"requirements": ["SQLAlchemy==2.0.36", "sqlparse==0.5.0"]
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ pyudev==0.24.1
|
||||
PyYAML==6.0.2
|
||||
requests==2.32.3
|
||||
securetar==2024.11.0
|
||||
SQLAlchemy==2.0.31
|
||||
SQLAlchemy==2.0.36
|
||||
standard-aifc==3.13.0;python_version>='3.13'
|
||||
standard-telnetlib==3.13.0;python_version>='3.13'
|
||||
typing-extensions>=4.12.2,<5.0
|
||||
|
@ -66,7 +66,7 @@ dependencies = [
|
||||
"PyYAML==6.0.2",
|
||||
"requests==2.32.3",
|
||||
"securetar==2024.11.0",
|
||||
"SQLAlchemy==2.0.31",
|
||||
"SQLAlchemy==2.0.36",
|
||||
"standard-aifc==3.13.0;python_version>='3.13'",
|
||||
"standard-telnetlib==3.13.0;python_version>='3.13'",
|
||||
"typing-extensions>=4.12.2,<5.0",
|
||||
|
@ -37,7 +37,7 @@ python-slugify==8.0.4
|
||||
PyYAML==6.0.2
|
||||
requests==2.32.3
|
||||
securetar==2024.11.0
|
||||
SQLAlchemy==2.0.31
|
||||
SQLAlchemy==2.0.36
|
||||
standard-aifc==3.13.0;python_version>='3.13'
|
||||
standard-telnetlib==3.13.0;python_version>='3.13'
|
||||
typing-extensions>=4.12.2,<5.0
|
||||
|
@ -116,7 +116,7 @@ RtmAPI==0.7.2
|
||||
|
||||
# homeassistant.components.recorder
|
||||
# homeassistant.components.sql
|
||||
SQLAlchemy==2.0.31
|
||||
SQLAlchemy==2.0.36
|
||||
|
||||
# homeassistant.components.tami4
|
||||
Tami4EdgeAPI==3.0
|
||||
|
@ -110,7 +110,7 @@ RtmAPI==0.7.2
|
||||
|
||||
# homeassistant.components.recorder
|
||||
# homeassistant.components.sql
|
||||
SQLAlchemy==2.0.31
|
||||
SQLAlchemy==2.0.36
|
||||
|
||||
# homeassistant.components.tami4
|
||||
Tami4EdgeAPI==3.0
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
@ -597,9 +598,6 @@ async def test_options_flow_db_url_empty(
|
||||
"homeassistant.components.sql.async_setup_entry",
|
||||
return_value=True,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.sql.config_flow.sqlalchemy.create_engine",
|
||||
),
|
||||
):
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
@ -621,7 +619,9 @@ async def test_options_flow_db_url_empty(
|
||||
|
||||
|
||||
async def test_full_flow_not_recorder_db(
|
||||
recorder_mock: Recorder, hass: HomeAssistant
|
||||
recorder_mock: Recorder,
|
||||
hass: HomeAssistant,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Test full config flow with not using recorder db."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@ -629,20 +629,19 @@ async def test_full_flow_not_recorder_db(
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {}
|
||||
db_path = tmp_path / "db.db"
|
||||
db_path_str = f"sqlite:///{db_path}"
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.sql.async_setup_entry",
|
||||
return_value=True,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.sql.config_flow.sqlalchemy.create_engine",
|
||||
),
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
"db_url": "sqlite://path/to/db.db",
|
||||
"db_url": db_path_str,
|
||||
"name": "Get Value",
|
||||
"query": "SELECT 5 as value",
|
||||
"column": "value",
|
||||
@ -654,7 +653,7 @@ async def test_full_flow_not_recorder_db(
|
||||
assert result2["title"] == "Get Value"
|
||||
assert result2["options"] == {
|
||||
"name": "Get Value",
|
||||
"db_url": "sqlite://path/to/db.db",
|
||||
"db_url": db_path_str,
|
||||
"query": "SELECT 5 as value",
|
||||
"column": "value",
|
||||
}
|
||||
@ -671,15 +670,12 @@ async def test_full_flow_not_recorder_db(
|
||||
"homeassistant.components.sql.async_setup_entry",
|
||||
return_value=True,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.sql.config_flow.sqlalchemy.create_engine",
|
||||
),
|
||||
):
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
"query": "SELECT 5 as value",
|
||||
"db_url": "sqlite://path/to/db.db",
|
||||
"db_url": db_path_str,
|
||||
"column": "value",
|
||||
"unit_of_measurement": "MiB",
|
||||
},
|
||||
@ -689,7 +685,7 @@ async def test_full_flow_not_recorder_db(
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["data"] == {
|
||||
"name": "Get Value",
|
||||
"db_url": "sqlite://path/to/db.db",
|
||||
"db_url": db_path_str,
|
||||
"query": "SELECT 5 as value",
|
||||
"column": "value",
|
||||
"unit_of_measurement": "MiB",
|
||||
@ -697,24 +693,22 @@ async def test_full_flow_not_recorder_db(
|
||||
|
||||
# Need to test same again to mitigate issue with db_url removal
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
with patch(
|
||||
"homeassistant.components.sql.config_flow.sqlalchemy.create_engine",
|
||||
):
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
"query": "SELECT 5 as value",
|
||||
"db_url": "sqlite://path/to/db.db",
|
||||
"column": "value",
|
||||
"unit_of_measurement": "MB",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
"query": "SELECT 5 as value",
|
||||
"db_url": db_path_str,
|
||||
"column": "value",
|
||||
"unit_of_measurement": "MB",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["data"] == {
|
||||
"name": "Get Value",
|
||||
"db_url": "sqlite://path/to/db.db",
|
||||
"db_url": db_path_str,
|
||||
"query": "SELECT 5 as value",
|
||||
"column": "value",
|
||||
"unit_of_measurement": "MB",
|
||||
@ -722,7 +716,7 @@ async def test_full_flow_not_recorder_db(
|
||||
|
||||
assert entry.options == {
|
||||
"name": "Get Value",
|
||||
"db_url": "sqlite://path/to/db.db",
|
||||
"db_url": db_path_str,
|
||||
"query": "SELECT 5 as value",
|
||||
"column": "value",
|
||||
"unit_of_measurement": "MB",
|
||||
|
@ -3,12 +3,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from pathlib import Path
|
||||
import sqlite3
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from sqlalchemy import text as sql_text
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from homeassistant.components.recorder import Recorder
|
||||
@ -143,29 +144,37 @@ async def test_query_no_value(
|
||||
assert text in caplog.text
|
||||
|
||||
|
||||
async def test_query_mssql_no_result(
|
||||
recorder_mock: Recorder, hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
||||
async def test_query_on_disk_sqlite_no_result(
|
||||
recorder_mock: Recorder,
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Test the SQL sensor with a query that returns no value."""
|
||||
config = {
|
||||
"db_url": "mssql://",
|
||||
"query": "SELECT 5 as value where 1=2",
|
||||
"column": "value",
|
||||
"name": "count_tables",
|
||||
}
|
||||
with (
|
||||
patch("homeassistant.components.sql.sensor.sqlalchemy"),
|
||||
patch(
|
||||
"homeassistant.components.sql.sensor.sqlalchemy.text",
|
||||
return_value=sql_text("SELECT TOP 1 5 as value where 1=2"),
|
||||
),
|
||||
):
|
||||
await init_integration(hass, config)
|
||||
db_path = tmp_path / "test.db"
|
||||
db_path_str = f"sqlite:///{db_path}"
|
||||
|
||||
state = hass.states.get("sensor.count_tables")
|
||||
def make_test_db():
|
||||
"""Create a test database."""
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.execute("CREATE TABLE users (value INTEGER)")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
await hass.async_add_executor_job(make_test_db)
|
||||
|
||||
config = {
|
||||
"db_url": db_path_str,
|
||||
"query": "SELECT value from users",
|
||||
"column": "value",
|
||||
"name": "count_users",
|
||||
}
|
||||
await init_integration(hass, config)
|
||||
|
||||
state = hass.states.get("sensor.count_users")
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
text = "SELECT TOP 1 5 AS VALUE WHERE 1=2 returned no results"
|
||||
text = "SELECT value from users LIMIT 1; returned no results"
|
||||
assert text in caplog.text
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user