From 814c46c83e7408ba6e89444fcea274c57f89f1e4 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Wed, 13 Nov 2013 10:03:55 -0500 Subject: [PATCH] - support SQLAlchemy ticket 2742 by ensuring that expressions in DDL are rendered (at least in indexes for now) using literal_binds=True, resolve #155 --- alembic/autogenerate/render.py | 17 ++++++++++++++-- alembic/compat.py | 3 +++ tests/test_autogenerate.py | 37 ++++++++++++++++++++++++++++++++-- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/alembic/autogenerate/render.py b/alembic/autogenerate/render.py index c471182b..0f31dbca 100644 --- a/alembic/autogenerate/render.py +++ b/alembic/autogenerate/render.py @@ -1,5 +1,6 @@ -from sqlalchemy import schema as sa_schema, types as sqltypes +from sqlalchemy import schema as sa_schema, types as sqltypes, sql import logging +from .. import compat import re @@ -7,6 +8,17 @@ from ..compat import string_types, get_index_column_names log = logging.getLogger(__name__) +def _render_potential_expr(value, autogen_context): + if isinstance(value, sql.ClauseElement): + if compat.sqla_08: + return str( + value.compile(dialect=autogen_context['dialect'], + compile_kwargs={'literal_binds': True})) + else: + return str(value.compile(dialect=autogen_context['dialect'])) + else: + return str(value) + def _add_table(table, autogen_context): text = "%(prefix)screate_table(%(tablename)r,\n%(args)s" % { 'tablename': table.name, @@ -51,7 +63,8 @@ def _add_index(index, autogen_context): 'unique': index.unique or False, 'schema': (", schema='%s'" % index.table.schema) if index.table.schema else '', 'kwargs': (', '+', '.join( - ["%s='%s'" % (key, val) for key, val in index.kwargs.items()]))\ + ["%s=%r" % (key, _render_potential_expr(val, autogen_context)) + for key, val in index.kwargs.items()]))\ if len(index.kwargs) else '' } return text diff --git a/alembic/compat.py b/alembic/compat.py index 7d0357dc..0f066aa9 100644 --- a/alembic/compat.py +++ b/alembic/compat.py @@ -4,6 +4,9 @@ from sqlalchemy import __version__ as sa_version if sys.version_info < (2, 6): raise NotImplementedError("Python 2.6 or greater is required.") +sqla_08 = sa_version >= '0.8.0' +sqla_09 = sa_version >= '0.9.0' + py2k = sys.version_info < (3, 0) py3k = sys.version_info >= (3, 0) py33 = sys.version_info >= (3, 3) diff --git a/tests/test_autogenerate.py b/tests/test_autogenerate.py index aaaebd21..d61945db 100644 --- a/tests/test_autogenerate.py +++ b/tests/test_autogenerate.py @@ -9,11 +9,11 @@ from sqlalchemy import MetaData, Column, Table, Integer, String, Text, \ UniqueConstraint, Boolean, ForeignKeyConstraint,\ PrimaryKeyConstraint, Index, func from sqlalchemy.types import NULLTYPE, TIMESTAMP -from sqlalchemy.dialects import mysql +from sqlalchemy.dialects import mysql, postgresql from sqlalchemy.engine.reflection import Inspector from sqlalchemy.sql import and_, column, literal_column -from alembic import autogenerate, util +from alembic import autogenerate, util, compat from alembic.migration import MigrationContext from . import staging_env, sqlite_db, clear_staging_env, eq_, \ eq_ignore_whitespace, requires_07, db_for_dialect @@ -1078,6 +1078,14 @@ class AutogenRenderTest(TestCase): }, 'dialect': mysql.dialect() } + cls.pg_autogen_context = { + 'opts': { + 'sqlalchemy_module_prefix': 'sa.', + 'alembic_module_prefix': 'op.', + }, + 'dialect': postgresql.dialect() + } + def test_render_add_index(self): """ @@ -1114,6 +1122,31 @@ class AutogenRenderTest(TestCase): "['active', 'code'], unique=False, schema='CamelSchema')" ) + def test_render_add_index_pg_where(self): + autogen_context = self.pg_autogen_context + + m = MetaData() + t = Table('t', m, + Column('x', String), + Column('y', String) + ) + + idx = Index('foo_idx', t.c.x, t.c.y, + postgresql_where=(t.c.y == 'something')) + + if compat.sqla_08: + eq_ignore_whitespace( + autogenerate.render._add_index(idx, autogen_context), + """op.create_index('foo_idx', 't', ['x', 'y'], unique=False, """ + """postgresql_where="t.y = 'something'")""" + ) + else: + eq_ignore_whitespace( + autogenerate.render._add_index(idx, autogen_context), + """op.create_index('foo_idx', 't', ['x', 'y'], unique=False, """ + """postgresql_where='t.y = %(y_1)s')""" + ) + # def test_render_add_index_func(self): # """ # autogenerate.render._drop_index using func -- TODO: SQLA needs to -- 2.47.2