]> git.ipfire.org Git - thirdparty/sqlalchemy/alembic.git/commitdiff
Render ExcludeContraint Column as column, not plain string
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 22 Jan 2018 19:40:23 +0000 (14:40 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 22 Jan 2018 20:08:57 +0000 (15:08 -0500)
Fixed bug where autogenerate of :class:`.ExcludeConstraint`
would render a raw quoted name for a Column that has case-sensitive
characters, which when invoked as an inline member of the Table
would produce a stack trace that the quoted name is not found.
An incoming Column object is now rendered as ``sa.column('name')``.

Change-Id: Ic84fc0b0fbaa5816ece1944043cd01a653bfe4ce
Fixes: #478
alembic/ddl/postgresql.py
docs/build/unreleased/478.rst [new file with mode: 0644]
tests/test_postgresql.py

index e42bc8105448ec73373f0614f8f6637da63b0d2f..7f582b3999257061d03e5ccfdd10909c303e00b3 100644 (file)
@@ -8,6 +8,7 @@ from .impl import DefaultImpl
 from sqlalchemy.dialects.postgresql import INTEGER, BIGINT
 from ..autogenerate import render
 from sqlalchemy import text, Numeric, Column
+from sqlalchemy.sql.expression import ColumnClause
 from sqlalchemy.types import NULLTYPE
 from sqlalchemy import types as sqltypes
 
@@ -416,8 +417,7 @@ def _exclude_constraint(constraint, autogen_context, alter):
             args += [repr(render._ident(constraint.table.name))]
         args.extend([
             "(%s, %r)" % (
-                render._render_potential_expr(
-                    sqltext, autogen_context, wrap_in_text=False),
+                _render_potential_column(sqltext, autogen_context),
                 opstring
             )
             for sqltext, name, opstring in constraint._render_exprs
@@ -435,8 +435,7 @@ def _exclude_constraint(constraint, autogen_context, alter):
     else:
         args = [
             "(%s, %r)" % (
-                render._render_potential_expr(
-                    sqltext, autogen_context, wrap_in_text=False),
+                _render_potential_column(sqltext, autogen_context),
                 opstring
             ) for sqltext, name, opstring in constraint._render_exprs
         ]
@@ -450,3 +449,16 @@ def _exclude_constraint(constraint, autogen_context, alter):
             "prefix": _postgresql_autogenerate_prefix(autogen_context),
             "args": ", ".join(args)
         }
+
+
+def _render_potential_column(value, autogen_context):
+    if isinstance(value, ColumnClause):
+        template = "%(prefix)scolumn(%(name)r)"
+
+        return template % {
+            "prefix": render._sqlalchemy_autogenerate_prefix(autogen_context),
+            "name": value.name
+        }
+
+    else:
+        return render._render_potential_expr(value, autogen_context, wrap_in_text=False)
diff --git a/docs/build/unreleased/478.rst b/docs/build/unreleased/478.rst
new file mode 100644 (file)
index 0000000..8c479b0
--- /dev/null
@@ -0,0 +1,9 @@
+.. change::
+    :tags: bug, postgresql, autogenerate
+    :tickets: 478
+
+    Fixed bug where autogenerate of :class:`.ExcludeConstraint`
+    would render a raw quoted name for a Column that has case-sensitive
+    characters, which when invoked as an inline member of the Table
+    would produce a stack trace that the quoted name is not found.
+    An incoming Column object is now rendered as ``sa.column('name')``.
index e60a02d657ccd8a7a6dfe00fe23025c9ab6b3532..ae74c0866710adaf84afa496bf5967311faff934 100644 (file)
@@ -760,10 +760,36 @@ unique=False, """
 
         eq_ignore_whitespace(
             autogenerate.render_op_text(autogen_context, op_obj),
-            "op.create_exclude_constraint('t_excl_x', 't', ('x', '>'), "
+            "op.create_exclude_constraint('t_excl_x', 't', (sa.column('x'), '>'), "
             "where=sa.text(!U'x != 2'), using='gist')"
         )
 
+    @config.requirements.fail_before_sqla_100
+    def test_add_exclude_constraint_case_sensitive(self):
+        from sqlalchemy.dialects.postgresql import ExcludeConstraint
+
+        autogen_context = self.autogen_context
+
+        m = MetaData()
+        t = Table('TTAble', m,
+                  Column('XColumn', String),
+                  Column('YColumn', String)
+                  )
+
+        op_obj = ops.AddConstraintOp.from_constraint(ExcludeConstraint(
+            (t.c.XColumn, ">"),
+            where=t.c.XColumn != 2,
+            using="gist",
+            name="t_excl_x"
+        ))
+
+        eq_ignore_whitespace(
+            autogenerate.render_op_text(autogen_context, op_obj),
+            "op.create_exclude_constraint('t_excl_x', 'TTAble', (sa.column('XColumn'), '>'), "
+            "where=sa.text(!U'\"XColumn\" != 2'), using='gist')"
+        )
+
+
     @config.requirements.fail_before_sqla_100
     def test_inline_exclude_constraint(self):
         from sqlalchemy.dialects.postgresql import ExcludeConstraint
@@ -794,6 +820,37 @@ unique=False, """
             ")"
         )
 
+    @config.requirements.fail_before_sqla_100
+    def test_inline_exclude_constraint_case_sensitive(self):
+        from sqlalchemy.dialects.postgresql import ExcludeConstraint
+
+        autogen_context = self.autogen_context
+
+        m = MetaData()
+        t = Table(
+            'TTable', m,
+            Column('XColumn', String),
+            Column('YColumn', String),
+        )
+        ExcludeConstraint(
+            (t.c.XColumn, ">"),
+            using="gist",
+            where='"XColumn" != 2',
+            name="TExclX"
+        )
+
+        op_obj = ops.CreateTableOp.from_table(t)
+
+        eq_ignore_whitespace(
+            autogenerate.render_op_text(autogen_context, op_obj),
+            "op.create_table('TTable',sa.Column('XColumn', sa.String(), "
+            "nullable=True),"
+            "sa.Column('YColumn', sa.String(), nullable=True),"
+            "postgresql.ExcludeConstraint((sa.column('XColumn'), '>'), "
+            "where=sa.text(!U'\"XColumn\" != 2'), using='gist', "
+            "name='TExclX'))"
+        )
+
     @config.requirements.sqlalchemy_09
     def test_json_type(self):
         if config.requirements.sqlalchemy_110.enabled: