]> git.ipfire.org Git - thirdparty/sqlalchemy/alembic.git/commitdiff
Handle literal_column() in ExcludeConstraint differently
authorJan Katins <jasc@gmx.net>
Tue, 21 Feb 2023 21:15:03 +0000 (22:15 +0100)
committerJan Katins <jasc@gmx.net>
Fri, 3 Mar 2023 11:27:28 +0000 (12:27 +0100)
Before the change, a literal_column in an ExcludeConstraint ended up as a `column("...")`which in turn meant that the actual migration errored with

```
sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedColumn) column "id + 2" named in key does not exist

[SQL:
CREATE TABLE whatever2 (
        id BIGSERIAL NOT NULL,
        PRIMARY KEY (id),
        CONSTRAINT whatever_id_int8range_excl EXCLUDE USING gist (id WITH =, "id + 2" WITH =)
)
```

Note: the column name is interpreted as a quoted column named "id + 2".

After the fix, a literal_column is passed through, ending up in the correct SQL, which can be run on PG.

alembic/ddl/postgresql.py
tests/test_postgresql.py

index 994d7cbf22caa54f3f8cbda0a160afc14df04e3c..877338b6c1ebdcb35071f78a12b00f171a9dddf6 100644 (file)
@@ -670,7 +670,11 @@ def _render_potential_column(
     value: Union[ColumnClause, Column], autogen_context: AutogenContext
 ) -> str:
     if isinstance(value, ColumnClause):
-        template = "%(prefix)scolumn(%(name)r)"
+        if value.is_literal:
+            # Support stuff like literal_column("int8range(from, to)") in ExcludeConstraint
+            template = "%(prefix)sliteral_column(%(name)r)"
+        else:
+            template = "%(prefix)scolumn(%(name)r)"
 
         return template % {
             "prefix": render._sqlalchemy_autogenerate_prefix(autogen_context),
index a8c284de4d12e60f0b35ea685b5f470b7d8e11fd..8e901c55c93f451a42c385b65755eaf5856d5cb2 100644 (file)
@@ -28,6 +28,7 @@ from sqlalchemy.dialects.postgresql import UUID
 from sqlalchemy.sql import column
 from sqlalchemy.sql import false
 from sqlalchemy.sql import table
+from sqlalchemy.sql.expression import literal_column
 
 from alembic import autogenerate
 from alembic import command
@@ -1156,6 +1157,31 @@ class PostgresqlAutogenRenderTest(TestBase):
             "name='TExclX'))",
         )
 
+    def test_inline_exclude_constraint_literal_column(self):
+        # Issue: https://github.com/sqlalchemy/alembic/issues/1184
+        from sqlalchemy.dialects.postgresql import ExcludeConstraint
+
+        autogen_context = self.autogen_context
+
+        m = MetaData()
+        t = Table(
+            "TTable", m, Column("id", String()),
+            ExcludeConstraint(
+                (literal_column("id + 2"), "="), name="TExclID", using="gist"
+            ),
+        )
+
+        op_obj = ops.CreateTableOp.from_table(t)
+
+        eq_ignore_whitespace(
+            autogenerate.render_op_text(autogen_context, op_obj),
+            "op.create_table('TTable',sa.Column('id', sa.String(), "
+            "nullable=True),"
+            "postgresql.ExcludeConstraint((sa.literal_column('id + 2'), '='), "
+            "using='gist', "
+            "name='TExclID'))",
+        )
+
     def test_json_type(self):
         eq_ignore_whitespace(
             autogenerate.render._repr_type(JSON(), self.autogen_context),