diff --git a/alembic/autogenerate/compare/server_defaults.py b/alembic/autogenerate/compare/server_defaults.py index 1e09e8e21..ed9136979 100644 --- a/alembic/autogenerate/compare/server_defaults.py +++ b/alembic/autogenerate/compare/server_defaults.py @@ -21,6 +21,7 @@ if TYPE_CHECKING: from sqlalchemy.sql.elements import quoted_name from sqlalchemy.sql.schema import Column + from sqlalchemy.engine import Dialect from ...autogenerate.api import AutogenContext from ...operations.ops import AlterColumnOp @@ -48,14 +49,20 @@ def _render_server_default_for_compare( return None -def _normalize_computed_default(sqltext: str) -> str: +def _normalize_computed_default(sqltext: str, dialect: Dialect) -> str: """we want to warn if a computed sql expression has changed. however we don't want false positives and the warning is not that critical. so filter out most forms of variability from the SQL text. """ - return re.sub(r"[ \(\)'\"`\[\]\t\r\n]", "", sqltext).lower() + normalized_sqltext = re.sub(r"[ \(\)'\"`\[\]\t\r\n]", "", sqltext).lower() + if dialect.name == "postgresql": + # strip postgresql type cast specifiers, e.g. ``::regconfig``, + # ``::text``, which can appear inconsistently on the reflected side + # only and cause false-positive warnings. + normalized_sqltext = re.sub(r"::\w+", "", normalized_sqltext) + return normalized_sqltext def _compare_computed_default( @@ -94,7 +101,7 @@ def _compare_computed_default( # get a minimal comparison just to emit a warning. rendered_metadata_default = _normalize_computed_default( - rendered_metadata_default + rendered_metadata_default, autogen_context.dialect ) if isinstance(conn_col.server_default, sa_schema.Computed): @@ -105,7 +112,7 @@ def _compare_computed_default( ) ) rendered_conn_default = _normalize_computed_default( - rendered_conn_default + rendered_conn_default, autogen_context.dialect ) else: rendered_conn_default = "" diff --git a/docs/build/unreleased/1462.rst b/docs/build/unreleased/1462.rst new file mode 100644 index 000000000..fd919066e --- /dev/null +++ b/docs/build/unreleased/1462.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, autogenerate, postgresql + :tickets: 1462 + + Fixed false-positive "Computed default cannot be modified" warning emitted + during autogenerate when comparing PostgreSQL ``Computed`` columns whose + reflected SQL expression includes type cast specifiers such as + ``::regconfig`` or ``::text``. These cast specifiers are now stripped + before comparison so that semantically equivalent expressions no longer + trigger the warning. diff --git a/tests/test_postgresql.py b/tests/test_postgresql.py index d0304651d..76b84a541 100644 --- a/tests/test_postgresql.py +++ b/tests/test_postgresql.py @@ -21,6 +21,7 @@ from sqlalchemy import text from sqlalchemy import types from sqlalchemy import UniqueConstraint +from sqlalchemy.dialects import postgresql from sqlalchemy.dialects.postgresql import ARRAY from sqlalchemy.dialects.postgresql import BYTEA from sqlalchemy.dialects.postgresql import ExcludeConstraint @@ -72,6 +73,9 @@ from alembic.autogenerate.compare.server_defaults import ( _dialect_impl_compare_server_default as _compare_server_default, ) + from alembic.autogenerate.compare.server_defaults import ( + _normalize_computed_default, + ) class PostgresqlOpTest(TestBase): @@ -395,6 +399,22 @@ def test_alter_column_computed_not_supported(self, sd, esd): existing_server_default=esd(), ) + def test_normalize_computed_default_strips_type_casts(self): + # type cast specifiers such as ``::regconfig`` only appear on the + # reflected (connection) side and would otherwise cause a + # false-positive "cannot be modified" warning. issue #1462 + dialect = postgresql.dialect() + eq_( + _normalize_computed_default( + "setweight(to_tsvector('english', title), 'a')", dialect + ), + _normalize_computed_default( + "setweight(to_tsvector('english'::regconfig, " + "title::text), 'a'::\"char\")", + dialect, + ), + ) + @combinations( ({}, None), (dict(always=True), None),