You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
alembic autogeneration "false-positively" detects change to comments from the empty string to None #1085
Describe the bug
Setting column comment to "" causes autogenerate to create changes even though no changes were made.
Expected behavior
I expected Column("mycol", sa.types.String(), comment="") to be treated as it is, and that sequential autogenerate commands would generate empty migrations.
To Reproduce
Please try to provide a Minimal, Complete, and Verifiable example, with the migration script and/or the SQLAlchemy tables or models involved.
See also Reporting Bugs on the website.
this is probably because somewhere there is something like an if comment and somewhere else there is a if commnet is not None.
Thanks for reporting, but I'm not sure this warrants a fix, since the workaround is trivial
Full runnable reproduction (no database needed — the sqlite dialect reports supports_comments=False, which short-circuits the whole comparison path, so the comparators have to be exercised directly):
fromtypesimportSimpleNamespacefromsqlalchemyimportColumn, String, MetaData, Tablefromalembic.operationsimportopsfromalembic.util.langhelpersimportPriorityDispatchResultfromalembic.autogenerate.compare.commentsimport (
_compare_column_comment,
_compare_table_comment,
)
ctx=SimpleNamespace(dialect=SimpleNamespace(supports_comments=True))
# column: what the DB reflects back (None) vs what the model has ("")alter=ops.AlterTableOp("t")
res=_compare_column_comment(
ctx, alter, None, "t", "c",
Column("c", String(), comment=None),
Column("c", String(), comment=""),
)
print("column None vs '' ->", res) # STOP (spurious) on current main# table: same, through the ops listmops=ops.ModifyTableOps("t", [])
res=_compare_table_comment(
ctx, mops, None, "t",
Table("t", MetaData(), Column("c", String()), comment=None),
Table("t", MetaData(), Column("c", String()), comment=""),
)
print("table None vs '' ->", res, mops.ops) # STOP + CreateTableCommentOp (spurious)
On current main both print STOP (false positive). With the fix in #1867 both print CONTINUE, while genuine comment changes ("x" vs None, "a" vs "b") still print STOP.
PR #1867 implements the fix (normalize "" to None in both comparators, plus a regression test along the lines above) but was auto-closed by the prgate bot because this issue is not marked open for pull requests yet — it is ready to be reopened once the label is added.
Hi, just reproduced this on current main (Windows, Python 3.12, SQLAlchemy 2.0.55). Both the column and table cases return STOP for None vs '', and the table path even emits a CreateTableCommentOp, so every autogenerate run proposes a phantom migration. Genuine changes (None vs 'hello') still return STOP as expected.
This bites us because our models use comment='' in a few places and the diff never comes back clean. Would you consider adding the open for pull requests label here? PR #1867 already implements the fix and looks ready to reopen once labeled. Happy to pick it up and carry it forward myself if the original author is away, either by reopening that one or opening a fresh PR, whichever you prefer.
Thanks, agreed, #1867 is the wrong direction, None vs empty string should stay distinct.
If I read it right, the loop is this: create_table render plus impl.create_table / toimpl.add_column all use if comment: so empty string is never emitted, reflect comes back None, comparator correctly fires STOP every run.
I have a local fix ready that preserves empty string with is not None in autogenerate/render.py _add_table / _render_column and ddl/impl.py create_table plus operations/toimpl.py add_column and ddl/mysql.py _mysql_colspec, with render now emitting comment=''. Comparators untouched so genuine None to empty string changes still diff. Tests: test_autogen_render, test_op, test_autogen_diffs green.
Does that match what you meant by make create table render the empty comment? If so, can you add open for pull requests here and I will put it up?
Update: opened #1871 with this approach. Empty string is now preserved with is not None in render and DDL paths, comparators untouched except Oracle where empty string is normalized to none since it equals NULL there. Ready for review.
Activity
CaselIT commentedon Sep 7, 2022
Hi,
this is probably because somewhere there is something like an
if commentand somewhere else there is aif commnet is not None.Thanks for reporting, but I'm not sure this warrants a fix, since the workaround is trivial
IMGillusion commentedon Sep 6, 2026
Full runnable reproduction (no database needed — the sqlite dialect reports
supports_comments=False, which short-circuits the whole comparison path, so the comparators have to be exercised directly):On current
mainboth printSTOP(false positive). With the fix in #1867 both printCONTINUE, while genuine comment changes ("x"vsNone,"a"vs"b") still printSTOP.PR #1867 implements the fix (normalize
""toNonein both comparators, plus a regression test along the lines above) but was auto-closed by the prgate bot because this issue is not marked open for pull requests yet — it is ready to be reopened once the label is added.vineoy commentedon Sep 29, 2026
Hi, just reproduced this on current main (Windows, Python 3.12, SQLAlchemy 2.0.55). Both the column and table cases return STOP for None vs '', and the table path even emits a CreateTableCommentOp, so every autogenerate run proposes a phantom migration. Genuine changes (None vs 'hello') still return STOP as expected.
This bites us because our models use comment='' in a few places and the diff never comes back clean. Would you consider adding the open for pull requests label here? PR #1867 already implements the fix and looks ready to reopen once labeled. Happy to pick it up and carry it forward myself if the original author is away, either by reopening that one or opening a fresh PR, whichever you prefer.
CaselIT commentedon Sep 30, 2026
That pr is wrong though. The fix here is not to treat none and "" as equal since they are not, it's to make create table render the empty comment
vineoy commentedon Sep 30, 2026
Thanks, agreed, #1867 is the wrong direction, None vs empty string should stay distinct.
If I read it right, the loop is this: create_table render plus impl.create_table / toimpl.add_column all use if comment: so empty string is never emitted, reflect comes back None, comparator correctly fires STOP every run.
I have a local fix ready that preserves empty string with is not None in autogenerate/render.py _add_table / _render_column and ddl/impl.py create_table plus operations/toimpl.py add_column and ddl/mysql.py _mysql_colspec, with render now emitting comment=''. Comparators untouched so genuine None to empty string changes still diff. Tests: test_autogen_render, test_op, test_autogen_diffs green.
Does that match what you meant by make create table render the empty comment? If so, can you add open for pull requests here and I will put it up?
CaselIT commentedon Sep 30, 2026
I think so, yes, I'll open for PR.
Oracle may need special casing here, since IIRC empty string and None are in fact equal on it
vineoy commentedon Sep 30, 2026
Update: opened #1871 with this approach. Empty string is now preserved with is not None in render and DDL paths, comparators untouched except Oracle where empty string is normalized to none since it equals NULL there. Ready for review.
Add empty comment render and compare tests for sqlalchemy#1085
sqla-tester commentedon Oct 1, 2026
vinayak has proposed a fix for this issue in the main branch:
Preserve empty string comments on create table and add column https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/7016
sqla-tester commentedon Oct 19, 2026
vinayak has proposed a fix for this issue in the main branch:
Preserve empty string comments on create table and add column https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/7016
zzzeek commentedon Oct 19, 2026
but they are "equal" for PG, MySQL, Oracle, likely lots of others.
CaselIT commentedon Oct 19, 2026
I stand corrected then. I guess the crux of the issue is not that '' and null are the same but that an empty comment is treated as no comment.
In pg it's even documented https://www.postgresql.org/docs/current/sql-comment.html
I guess the issue then is to ensure that alembic does the same conditionally on the db impl.