From: Mike Bayer Date: Sun, 2 Nov 2014 17:35:47 +0000 (-0500) Subject: - Added a workaround for SQLAlchemy issue #3023 (fixed in 0.9.5) where X-Git-Tag: rel_0_7_1~11^2~8 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=92184a55cac2c0fa04396df4b2d4d9694e97f43f;p=thirdparty%2Fsqlalchemy%2Falembic.git - Added a workaround for SQLAlchemy issue #3023 (fixed in 0.9.5) where a column that's part of an explicit PrimaryKeyConstraint would not have its "nullable" flag set to False, thus producing a false autogenerate. Also added a related correction to MySQL which will correct for MySQL's implicit server default of '0' when a NULL integer column is turned into a primary key column. fixes #199 --- diff --git a/alembic/autogenerate/compare.py b/alembic/autogenerate/compare.py index dbc7b9f3..1a87d667 100644 --- a/alembic/autogenerate/compare.py +++ b/alembic/autogenerate/compare.py @@ -167,11 +167,13 @@ def _compare_columns(schema, tname, object_filters, conn_table, metadata_table, metadata_col, col_diff, autogen_context ) - _compare_nullable(schema, tname, colname, - conn_col, - metadata_col.nullable, - col_diff, autogen_context - ) + # work around SQLAlchemy issue #3023 + if not metadata_col.primary_key: + _compare_nullable(schema, tname, colname, + conn_col, + metadata_col.nullable, + col_diff, autogen_context + ) _compare_server_default(schema, tname, colname, conn_col, metadata_col, diff --git a/alembic/ddl/mysql.py b/alembic/ddl/mysql.py index 07a32573..29973fe4 100644 --- a/alembic/ddl/mysql.py +++ b/alembic/ddl/mysql.py @@ -72,6 +72,22 @@ class MySQLImpl(DefaultImpl): ) ) + def compare_server_default(self, inspector_column, + metadata_column, + rendered_metadata_default, + rendered_inspector_default): + # partially a workaround for SQLAlchemy issue #3023; if the + # column were created without "NOT NULL", MySQL may have added + # an implicit default of '0' which we need to skip + if metadata_column.type._type_affinity is sqltypes.Integer and \ + inspector_column.primary_key and \ + not inspector_column.autoincrement and \ + not rendered_metadata_default and \ + rendered_inspector_default == "'0'": + return False + else: + return rendered_inspector_default != rendered_metadata_default + def correct_for_autogen_constraints(self, conn_unique_constraints, conn_indexes, metadata_unique_constraints, diff --git a/docs/build/changelog.rst b/docs/build/changelog.rst index e9a95bbd..042990b8 100644 --- a/docs/build/changelog.rst +++ b/docs/build/changelog.rst @@ -5,6 +5,17 @@ Changelog .. changelog:: :version: 0.7.0 + .. change:: + :tags: bug, autogenerate + :tickets: 199 + + Added a workaround for SQLAlchemy issue #3023 (fixed in 0.9.5) where + a column that's part of an explicit PrimaryKeyConstraint would not + have its "nullable" flag set to False, thus producing a false + autogenerate. Also added a related correction to MySQL which will + correct for MySQL's implicit server default of '0' when a NULL integer + column is turned into a primary key column. + .. change:: :tags: bug, autogenerate, mysql :tickets: 240 diff --git a/tests/test_autogenerate.py b/tests/test_autogenerate.py index ce239edd..1f25101d 100644 --- a/tests/test_autogenerate.py +++ b/tests/test_autogenerate.py @@ -3,7 +3,7 @@ import sys from sqlalchemy import MetaData, Column, Table, Integer, String, Text, \ Numeric, CHAR, ForeignKey, INTEGER, \ - TypeDecorator, CheckConstraint, text + TypeDecorator, CheckConstraint, text, PrimaryKeyConstraint from sqlalchemy.types import NULLTYPE from sqlalchemy.engine.reflection import Inspector @@ -38,7 +38,6 @@ def new_table(table, parent): class AutogenTest(object): - __only_on__ = 'sqlite' @classmethod def _get_bind(cls): @@ -370,6 +369,7 @@ class ModelOne(object): class AutogenerateDiffTest(ModelOne, AutogenTest, TestBase): + __only_on__ = 'sqlite' def test_diffs(self): """test generation of diff rules""" @@ -791,6 +791,7 @@ name='extra_uid_fkey'), class AutogenerateCustomCompareTypeTest(AutogenTest, TestBase): + __only_on__ = 'sqlite' @classmethod def _get_db_schema(cls): @@ -857,7 +858,40 @@ class AutogenerateCustomCompareTypeTest(AutogenTest, TestBase): eq_(diffs[1][0][0], 'modify_type') +class PKConstraintUpgradesIgnoresNullableTest(AutogenTest, TestBase): + __backend__ = True + + # test workaround for SQLAlchemy issue #3023, alembic issue #199 + @classmethod + def _get_db_schema(cls): + m = MetaData() + + Table( + 'person_to_role', m, + Column('person_id', Integer, autoincrement=False), + Column('role_id', Integer, autoincrement=False), + PrimaryKeyConstraint('person_id', 'role_id') + ) + return m + + @classmethod + def _get_model_schema(cls): + return cls._get_db_schema() + + def test_no_change(self): + metadata = self.m2 + connection = self.context.bind + + diffs = [] + + autogenerate._produce_net_changes(connection, metadata, diffs, + self.autogen_context + ) + eq_(diffs, []) + + class AutogenKeyTest(AutogenTest, TestBase): + __only_on__ = 'sqlite' @classmethod def _get_db_schema(cls): @@ -903,6 +937,7 @@ class AutogenKeyTest(AutogenTest, TestBase): class AutogenVersionTableTest(AutogenTest, TestBase): + __only_on__ = 'sqlite' version_table_name = 'alembic_version' version_table_schema = None @@ -957,6 +992,7 @@ class AutogenCustomVersionTableAndSchemaTest(AutogenVersionTableTest): class AutogenerateDiffOrderTest(AutogenTest, TestBase): + __only_on__ = 'sqlite' @classmethod def _get_db_schema(cls): @@ -996,6 +1032,7 @@ class AutogenerateDiffOrderTest(AutogenTest, TestBase): class CompareMetadataTest(ModelOne, AutogenTest, TestBase): + __only_on__ = 'sqlite' def test_compare_metadata(self): metadata = self.m2