Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions alembic/autogenerate/compare/server_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,15 @@ def _compare_computed_default(
if sqla_compat._server_default_is_computed(
conn_col_default
) and not sqla_compat._server_default_is_computed(metadata_default):
alter_column_op.existing_server_default = conn_col_default
_warn_computed_not_supported(tname, cname)
return PriorityDispatchResult.STOP

if not sqla_compat._server_default_is_computed(metadata_default):
return PriorityDispatchResult.CONTINUE

alter_column_op.existing_server_default = conn_col_default

rendered_metadata_default = str(
cast(sa_schema.Computed, metadata_col.server_default).sqltext.compile(
dialect=autogen_context.dialect,
Expand Down Expand Up @@ -145,6 +148,8 @@ def _compare_identity_default(
):
return PriorityDispatchResult.CONTINUE

alter_column_op.existing_server_default = conn_col_default

assert isinstance(
metadata_col.server_default,
(sa_schema.Identity, sa_schema.Sequence, NoneType),
Expand All @@ -170,9 +175,7 @@ def _compare_identity_default(
sorted(diff),
)

return PriorityDispatchResult.STOP

return PriorityDispatchResult.CONTINUE
return PriorityDispatchResult.STOP


def _user_compare_server_default(
Expand Down Expand Up @@ -312,27 +315,30 @@ def _setup_autoincrement(

def setup(plugin: Plugin) -> None:
plugin.add_autogenerate_comparator(
_user_compare_server_default,
_compare_computed_default,
"column",
"server_default",
priority=DispatchPriority.FIRST,
)

plugin.add_autogenerate_comparator(
_compare_computed_default,
_compare_identity_default,
"column",
"server_default",
priority=DispatchPriority.FIRST,
)

plugin.add_autogenerate_comparator(
_compare_identity_default,
_user_compare_server_default,
"column",
"server_default",
priority=DispatchPriority.MEDIUM,
)

plugin.add_autogenerate_comparator(
_setup_autoincrement,
"column",
"server_default",
"autoincrement",
)
plugin.add_autogenerate_comparator(
_dialect_impl_compare_server_default,
Expand Down
7 changes: 7 additions & 0 deletions docs/build/unreleased/1877.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.. change::
:tags: bug, autogenerate
:tickets: 1877

Fixed regression introduced in 1.18.0 where identity and computed column
comparisons, as well as autoincrement metadata setting, were skipped during
autogenerate when ``compare_server_default`` was not enabled.
122 changes: 122 additions & 0 deletions tests/test_autogen_diffs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from sqlalchemy import CHAR
from sqlalchemy import CheckConstraint
from sqlalchemy import Column
from sqlalchemy import Computed
from sqlalchemy import DATE
from sqlalchemy import DateTime
from sqlalchemy import DECIMAL
Expand All @@ -12,6 +13,7 @@
from sqlalchemy import ForeignKey
from sqlalchemy import ForeignKeyConstraint
from sqlalchemy import func
from sqlalchemy import Identity
from sqlalchemy import Index
from sqlalchemy import inspect
from sqlalchemy import INTEGER
Expand Down Expand Up @@ -943,6 +945,126 @@ def my_compare_server_default(
else:
assert not diff

def test_computed_warning_when_compare_server_default_false(
self, connection, metadata
):
t1 = Table(
"t1",
metadata,
Column("id", Integer, primary_key=True),
Column("x", Integer),
Column("y", Integer, Computed("x + 1")),
)
t1.create(connection)

new_metadata = MetaData()
Table(
"t1",
new_metadata,
Column("id", Integer, primary_key=True),
Column("x", Integer),
Column("y", Integer, Computed("x + 2")),
)

mc = MigrationContext.configure(
connection, opts={"compare_server_default": False}
)

with mock.patch("alembic.util.warn") as mock_warn:
diff = api.compare_metadata(mc, new_metadata)

eq_(
mock_warn.mock_calls,
[mock.call("Computed default on t1.y cannot be modified")],
)
eq_(diff, [])

def test_identity_compared_when_compare_server_default_false(
self, connection, metadata
):
t1 = Table(
"t1",
metadata,
Column("id", Integer, primary_key=True),
)
t1.create(connection)

new_metadata = MetaData()
Table(
"t1",
new_metadata,
Column("id", Integer, Identity(start=10), primary_key=True),
)

orig_get_columns = connection.dialect.get_columns

def mock_get_columns(conn, table_name, schema=None, **kw):
cols = orig_get_columns(conn, table_name, schema=schema, **kw)
if table_name == "t1":
for c in cols:
if c["name"] == "id":
c["identity"] = {"start": 1, "increment": 1}
return cols

mc = MigrationContext.configure(
connection, opts={"compare_server_default": False}
)

with mock.patch.object(
connection.dialect, "get_columns", side_effect=mock_get_columns
):
diff = api.compare_metadata(mc, new_metadata)

eq_(len(diff), 1)
eq_(diff[0][0][0], "modify_default")
eq_(diff[0][0][2], "t1")
eq_(diff[0][0][3], "id")
old = diff[0][0][5]
new = diff[0][0][6]
is_(isinstance(old, Identity), True)
eq_(old.start, 1)
eq_(old.increment, 1)
is_(isinstance(new, Identity), True)
eq_(new.start, 10)

def test_autoincrement_preserved_when_compare_server_default_false(
self, connection, metadata
):
t1 = Table(
"t1",
metadata,
Column("id", Integer, primary_key=True),
Column("x", Integer, server_default="0"),
)
t1.create(connection)

new_metadata = MetaData()
Table(
"t1",
new_metadata,
Column("id", Integer, primary_key=True),
Column("x", BigInteger, server_default="0", autoincrement=True),
)

mc = MigrationContext.configure(
connection,
opts={"compare_type": True, "compare_server_default": False},
)

script = api.produce_migrations(mc, new_metadata)
uo = script.upgrade_ops_list[-1]
modify_table_ops = [
op for op in uo.ops if isinstance(op, ops.ModifyTableOps)
]
eq_(len(modify_table_ops), 1)
alter_col_ops = [
op
for op in modify_table_ops[0].ops
if isinstance(op, ops.AlterColumnOp)
]
eq_(len(alter_col_ops), 1)
eq_(alter_col_ops[0].kw.get("autoincrement"), True)


class CompareMetadataToInspectorTest(TestBase):
__backend__ = True
Expand Down