From: Mike Bayer Date: Fri, 2 Jan 2026 19:19:46 +0000 (-0500) Subject: filter out ::regclass for sequences X-Git-Tag: rel_1_18_0~5 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=ff71a4f2a769f3ae8aaad40799bba29b23fa16ce;p=thirdparty%2Fsqlalchemy%2Falembic.git filter out ::regclass for sequences Fixed issue where PostgreSQL sequence defaults on non-primary key columns were incorrectly detected as changed on every autogenerate run. Server default comparison logic is adjusted to filter out the ``::regclass`` expression added by the server which interferes with the comparison. Fixes: #1507 Change-Id: I3192b2a86b1982dfbc7c85b49d676e1434dafcf5 --- diff --git a/alembic/ddl/postgresql.py b/alembic/ddl/postgresql.py index 8d036c64..18f95e4a 100644 --- a/alembic/ddl/postgresql.py +++ b/alembic/ddl/postgresql.py @@ -113,6 +113,7 @@ class PostgresqlImpl(DefaultImpl): rendered_metadata_default, rendered_inspector_default, ): + # don't do defaults for SERIAL columns if ( metadata_column.primary_key @@ -122,6 +123,11 @@ class PostgresqlImpl(DefaultImpl): conn_col_default = rendered_inspector_default + if conn_col_default and re.match( + r"nextval\('(.+?)'::regclass\)", conn_col_default + ): + conn_col_default = conn_col_default.replace("::regclass", "") + defaults_equal = conn_col_default == rendered_metadata_default if defaults_equal: return False @@ -143,6 +149,8 @@ class PostgresqlImpl(DefaultImpl): metadata_default = literal_column(metadata_default) # run a real compare against the server + # TODO: this seems quite a bad idea for a default that's a SQL + # function! SQL functions are not deterministic! conn = self.connection assert conn is not None return not conn.scalar( diff --git a/docs/build/unreleased/1507.rst b/docs/build/unreleased/1507.rst new file mode 100644 index 00000000..15ecef16 --- /dev/null +++ b/docs/build/unreleased/1507.rst @@ -0,0 +1,8 @@ +.. change:: + :tags: bug, postgresql + :tickets: 1507 + + Fixed issue where PostgreSQL sequence defaults on non-primary key columns + were incorrectly detected as changed on every autogenerate run. Server + default comparison logic is adjusted to filter out the ``::regclass`` + expression added by the server which interferes with the comparison. \ No newline at end of file diff --git a/tests/test_postgresql.py b/tests/test_postgresql.py index 6001d5d1..db9aa382 100644 --- a/tests/test_postgresql.py +++ b/tests/test_postgresql.py @@ -668,13 +668,13 @@ class PostgresqlDefaultCompareTest(TestBase): def setup_class(cls): cls.bind = config.db staging_env() - cls.migration_context = MigrationContext.configure( - connection=cls.bind.connect(), - opts={"compare_type": True, "compare_server_default": True}, - ) def setUp(self): self.metadata = MetaData() + self.migration_context = MigrationContext.configure( + connection=self.bind.connect(), + opts={"compare_type": True, "compare_server_default": True}, + ) self.autogen_context = api.AutogenContext(self.migration_context) @classmethod @@ -682,12 +682,18 @@ class PostgresqlDefaultCompareTest(TestBase): clear_staging_env() def tearDown(self): + self.migration_context.connection.close() + with config.db.begin() as conn: self.metadata.drop_all(conn) def _compare_default_roundtrip( self, type_, orig_default, alternate=None, diff_expected=None ): + # note this only tests compare_server_default including + # postgresql.compare_server_default. it does not run PG + # autogen_column_reflect() which is involved with omitting SERIAL + # columns diff_expected = ( diff_expected if diff_expected is not None @@ -854,6 +860,16 @@ class PostgresqlDefaultCompareTest(TestBase): ) assert not self._compare_default(t1, t2, t2.c.id, "") + def test_non_pk_sequence(self): + """test issue #1507""" + sequential_id_seq = Sequence( + "post_sequential_id_seq", metadata=self.metadata, start=1 + ) + sequential_id_seq.create(self.bind) + self._compare_default_roundtrip( + Integer, sequential_id_seq.next_value() + ) + class PostgresqlDetectSerialTest(TestBase): __only_on__ = "postgresql" @@ -943,8 +959,9 @@ class PostgresqlDetectSerialTest(TestBase): seq, ) - @testing.combinations((None,), ("test_schema",)) - def test_numeric(self, schema): + @testing.combinations((None,), ("test_schema",), argnames="schema") + @testing.variation("use_pk", [True, False]) + def test_numeric(self, schema, use_pk): seq = Sequence("x_id_seq", schema=schema) seq_name = seq.name if schema is None else f"{schema}.{seq.name}" self._expect_default( @@ -953,7 +970,7 @@ class PostgresqlDetectSerialTest(TestBase): "x", Numeric(8, 2), server_default=seq.next_value(), - primary_key=True, + primary_key=bool(use_pk), ), schema, seq,