From: Mike Bayer Date: Mon, 22 Jan 2018 19:40:23 +0000 (-0500) Subject: Render ExcludeContraint Column as column, not plain string X-Git-Tag: rel_0_9_8~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=70dfb13759d782d84aff94ae3a4ccb8f5d4fd4db;p=thirdparty%2Fsqlalchemy%2Falembic.git Render ExcludeContraint Column as column, not plain string 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 --- diff --git a/alembic/ddl/postgresql.py b/alembic/ddl/postgresql.py index e42bc810..7f582b39 100644 --- a/alembic/ddl/postgresql.py +++ b/alembic/ddl/postgresql.py @@ -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 index 00000000..8c479b0b --- /dev/null +++ b/docs/build/unreleased/478.rst @@ -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')``. diff --git a/tests/test_postgresql.py b/tests/test_postgresql.py index e60a02d6..ae74c086 100644 --- a/tests/test_postgresql.py +++ b/tests/test_postgresql.py @@ -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: