Ensure creating an index that already exists is forgiving for postgresql (#46185)
Unlikely sqlite and mysql, postgresql throws ProgrammingError instead of InternalError or OperationalError when trying to create an index that already exists.
This commit is contained in:
parent
6b340415b2
commit
dc26fd5149
2 changed files with 36 additions and 10 deletions
|
@ -3,7 +3,12 @@ import logging
|
||||||
|
|
||||||
from sqlalchemy import ForeignKeyConstraint, MetaData, Table, text
|
from sqlalchemy import ForeignKeyConstraint, MetaData, Table, text
|
||||||
from sqlalchemy.engine import reflection
|
from sqlalchemy.engine import reflection
|
||||||
from sqlalchemy.exc import InternalError, OperationalError, SQLAlchemyError
|
from sqlalchemy.exc import (
|
||||||
|
InternalError,
|
||||||
|
OperationalError,
|
||||||
|
ProgrammingError,
|
||||||
|
SQLAlchemyError,
|
||||||
|
)
|
||||||
from sqlalchemy.schema import AddConstraint, DropConstraint
|
from sqlalchemy.schema import AddConstraint, DropConstraint
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
@ -69,7 +74,7 @@ def _create_index(engine, table_name, index_name):
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
index.create(engine)
|
index.create(engine)
|
||||||
except OperationalError as err:
|
except (InternalError, ProgrammingError, OperationalError) as err:
|
||||||
lower_err_str = str(err).lower()
|
lower_err_str = str(err).lower()
|
||||||
|
|
||||||
if "already exists" not in lower_err_str and "duplicate" not in lower_err_str:
|
if "already exists" not in lower_err_str and "duplicate" not in lower_err_str:
|
||||||
|
@ -78,13 +83,6 @@ def _create_index(engine, table_name, index_name):
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Index %s already exists on %s, continuing", index_name, table_name
|
"Index %s already exists on %s, continuing", index_name, table_name
|
||||||
)
|
)
|
||||||
except InternalError as err:
|
|
||||||
if "duplicate" not in str(err).lower():
|
|
||||||
raise
|
|
||||||
|
|
||||||
_LOGGER.warning(
|
|
||||||
"Index %s already exists on %s, continuing", index_name, table_name
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER.debug("Finished creating %s", index_name)
|
_LOGGER.debug("Finished creating %s", index_name)
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
"""The tests for the Recorder component."""
|
"""The tests for the Recorder component."""
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
from unittest.mock import call, patch
|
from unittest.mock import Mock, PropertyMock, call, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.exc import InternalError, OperationalError, ProgrammingError
|
||||||
from sqlalchemy.pool import StaticPool
|
from sqlalchemy.pool import StaticPool
|
||||||
|
|
||||||
from homeassistant.bootstrap import async_setup_component
|
from homeassistant.bootstrap import async_setup_component
|
||||||
|
@ -79,3 +80,30 @@ def test_forgiving_add_index():
|
||||||
engine = create_engine("sqlite://", poolclass=StaticPool)
|
engine = create_engine("sqlite://", poolclass=StaticPool)
|
||||||
models.Base.metadata.create_all(engine)
|
models.Base.metadata.create_all(engine)
|
||||||
migration._create_index(engine, "states", "ix_states_context_id")
|
migration._create_index(engine, "states", "ix_states_context_id")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"exception_type", [OperationalError, ProgrammingError, InternalError]
|
||||||
|
)
|
||||||
|
def test_forgiving_add_index_with_other_db_types(caplog, exception_type):
|
||||||
|
"""Test that add index will continue if index exists on mysql and postgres."""
|
||||||
|
mocked_index = Mock()
|
||||||
|
type(mocked_index).name = "ix_states_context_id"
|
||||||
|
mocked_index.create = Mock(
|
||||||
|
side_effect=exception_type(
|
||||||
|
"CREATE INDEX ix_states_old_state_id ON states (old_state_id);",
|
||||||
|
[],
|
||||||
|
'relation "ix_states_old_state_id" already exists',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
mocked_table = Mock()
|
||||||
|
type(mocked_table).indexes = PropertyMock(return_value=[mocked_index])
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.recorder.migration.Table", return_value=mocked_table
|
||||||
|
):
|
||||||
|
migration._create_index(Mock(), "states", "ix_states_context_id")
|
||||||
|
|
||||||
|
assert "already exists on states" in caplog.text
|
||||||
|
assert "continuing" in caplog.text
|
||||||
|
|
Loading…
Add table
Reference in a new issue