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:
J. Nick Koston 2021-02-08 12:22:38 -10:00 committed by GitHub
parent 6b340415b2
commit dc26fd5149
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 36 additions and 10 deletions

View file

@ -3,7 +3,12 @@ import logging
from sqlalchemy import ForeignKeyConstraint, MetaData, Table, text
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 .const import DOMAIN
@ -69,7 +74,7 @@ def _create_index(engine, table_name, index_name):
)
try:
index.create(engine)
except OperationalError as err:
except (InternalError, ProgrammingError, OperationalError) as err:
lower_err_str = str(err).lower()
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(
"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)

View file

@ -1,9 +1,10 @@
"""The tests for the Recorder component."""
# pylint: disable=protected-access
from unittest.mock import call, patch
from unittest.mock import Mock, PropertyMock, call, patch
import pytest
from sqlalchemy import create_engine
from sqlalchemy.exc import InternalError, OperationalError, ProgrammingError
from sqlalchemy.pool import StaticPool
from homeassistant.bootstrap import async_setup_component
@ -79,3 +80,30 @@ def test_forgiving_add_index():
engine = create_engine("sqlite://", poolclass=StaticPool)
models.Base.metadata.create_all(engine)
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