]> git.ipfire.org Git - thirdparty/sqlalchemy/alembic.git/commitdiff
filter out ::regclass for sequences
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 2 Jan 2026 19:19:46 +0000 (14:19 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 2 Jan 2026 19:42:26 +0000 (14:42 -0500)
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

alembic/ddl/postgresql.py
docs/build/unreleased/1507.rst [new file with mode: 0644]
tests/test_postgresql.py

index 8d036c64044ee8a57d0148a87dbb7440a29bcf5a..18f95e4aa80acc07de76e04081b065095a579993 100644 (file)
@@ -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 (file)
index 0000000..15ecef1
--- /dev/null
@@ -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
index 6001d5d16f4b4f4a05b636cae0ff891b21b1018c..db9aa382235e0f8d60f019cc6c48cc5ce98443e0 100644 (file)
@@ -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,