mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Narrow sqlite database corruption check to ensure disk image is malformed (#121947)
* Narrow sqlite database corruption check to ensure disk image is malformed The database corruption check would also replace the database when it locked externally instead of only when its malformed. This was discovered in https://github.com/home-assistant/core/issues/121909#issuecomment-2227409124 when a user did a manual index creation while HA was online * tweak * tweak * fix * fix
This commit is contained in:
parent
19d2d023ab
commit
73f6e3c07b
@ -1182,7 +1182,15 @@ class Recorder(threading.Thread):
|
|||||||
|
|
||||||
def _handle_database_error(self, err: Exception) -> bool:
|
def _handle_database_error(self, err: Exception) -> bool:
|
||||||
"""Handle a database error that may result in moving away the corrupt db."""
|
"""Handle a database error that may result in moving away the corrupt db."""
|
||||||
if isinstance(err.__cause__, sqlite3.DatabaseError):
|
if (
|
||||||
|
(cause := err.__cause__)
|
||||||
|
and isinstance(cause, sqlite3.DatabaseError)
|
||||||
|
and (cause_str := str(cause))
|
||||||
|
# Make sure we do not move away a database when its only locked
|
||||||
|
# externally by another process. sqlite does not give us a named
|
||||||
|
# exception for this so we have to check the error message.
|
||||||
|
and ("malformed" in cause_str or "not a database" in cause_str)
|
||||||
|
):
|
||||||
_LOGGER.exception(
|
_LOGGER.exception(
|
||||||
"Unrecoverable sqlite3 database corruption detected: %s", err
|
"Unrecoverable sqlite3 database corruption detected: %s", err
|
||||||
)
|
)
|
||||||
|
@ -1699,7 +1699,9 @@ async def test_database_corruption_while_running(
|
|||||||
hass.states.async_set("test.lost", "on", {})
|
hass.states.async_set("test.lost", "on", {})
|
||||||
|
|
||||||
sqlite3_exception = DatabaseError("statement", {}, [])
|
sqlite3_exception = DatabaseError("statement", {}, [])
|
||||||
sqlite3_exception.__cause__ = sqlite3.DatabaseError()
|
sqlite3_exception.__cause__ = sqlite3.DatabaseError(
|
||||||
|
"database disk image is malformed"
|
||||||
|
)
|
||||||
|
|
||||||
await async_wait_recording_done(hass)
|
await async_wait_recording_done(hass)
|
||||||
with patch.object(
|
with patch.object(
|
||||||
|
@ -174,7 +174,9 @@ async def test_database_migration_encounters_corruption(
|
|||||||
assert recorder.util.async_migration_in_progress(hass) is False
|
assert recorder.util.async_migration_in_progress(hass) is False
|
||||||
|
|
||||||
sqlite3_exception = DatabaseError("statement", {}, [])
|
sqlite3_exception = DatabaseError("statement", {}, [])
|
||||||
sqlite3_exception.__cause__ = sqlite3.DatabaseError()
|
sqlite3_exception.__cause__ = sqlite3.DatabaseError(
|
||||||
|
"database disk image is malformed"
|
||||||
|
)
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch(
|
patch(
|
||||||
|
@ -204,7 +204,7 @@ async def test_purge_old_states_encouters_database_corruption(
|
|||||||
await async_wait_recording_done(hass)
|
await async_wait_recording_done(hass)
|
||||||
|
|
||||||
sqlite3_exception = DatabaseError("statement", {}, [])
|
sqlite3_exception = DatabaseError("statement", {}, [])
|
||||||
sqlite3_exception.__cause__ = sqlite3.DatabaseError()
|
sqlite3_exception.__cause__ = sqlite3.DatabaseError("not a database")
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch(
|
patch(
|
||||||
|
@ -178,7 +178,7 @@ async def test_purge_old_states_encouters_database_corruption(
|
|||||||
await async_wait_recording_done(hass)
|
await async_wait_recording_done(hass)
|
||||||
|
|
||||||
sqlite3_exception = DatabaseError("statement", {}, [])
|
sqlite3_exception = DatabaseError("statement", {}, [])
|
||||||
sqlite3_exception.__cause__ = sqlite3.DatabaseError()
|
sqlite3_exception.__cause__ = sqlite3.DatabaseError("not a database")
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch(
|
patch(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user