From: Mike Bayer Date: Sat, 7 Dec 2013 00:05:53 +0000 (-0500) Subject: - Added new argument ``mssql_drop_foreign_key`` to X-Git-Tag: rel_0_6_2~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=db44516cd9d6c12c2107a973d993ccec2810f17f;p=thirdparty%2Fsqlalchemy%2Falembic.git - Added new argument ``mssql_drop_foreign_key`` to :meth:`.Operations.drop_column`. Like ``mssql_drop_default`` and ``mssql_drop_check``, will do an inline lookup for a single foreign key which applies to this column, and drop it. For a column with more than one FK, you'd still need to explicitly use :meth:`.Operations.drop_constraint` given the name, even though only MSSQL has this limitation in the first place. --- diff --git a/alembic/ddl/mssql.py b/alembic/ddl/mssql.py index e4f2e9ae..095dbf34 100644 --- a/alembic/ddl/mssql.py +++ b/alembic/ddl/mssql.py @@ -116,6 +116,12 @@ class MSSQLImpl(DefaultImpl): table_name, column, 'sys.check_constraints') ) + drop_fks = kw.pop('mssql_drop_foreign_key', False) + if drop_fks: + self._exec( + _exec_drop_col_fk_constraint(self, + table_name, column) + ) super(MSSQLImpl, self).drop_column(table_name, column) def _exec_drop_col_constraint(impl, tname, colname, type_): @@ -126,11 +132,25 @@ select @const_name = [name] from %(type)s where parent_object_id = object_id('%(tname)s') and col_name(parent_object_id, parent_column_id) = '%(colname)s' exec('alter table %(tname)s drop constraint ' + @const_name)""" % { - 'type':type_, - 'tname':tname, - 'colname':colname + 'type': type_, + 'tname': tname, + 'colname': colname + } + +def _exec_drop_col_fk_constraint(impl, tname, colname): + return """declare @const_name varchar(256) +select @const_name = [name] from + sys.foreign_keys fk join sys.foreign_key_columns fkc + on fk.object_id=fkc.constraint_object_id +where fkc.parent_object_id = object_id('%(tname)s') +and col_name(fkc.parent_object_id, fkc.parent_column_id) = '%(colname)s' +exec('alter table %(tname)s drop constraint ' + @const_name)""" % { + 'tname': tname, + 'colname': colname } + + @compiles(AddColumn, 'mssql') def visit_add_column(element, compiler, **kw): return "%s %s" % ( diff --git a/alembic/operations.py b/alembic/operations.py index 221a764f..dfb942b4 100644 --- a/alembic/operations.py +++ b/alembic/operations.py @@ -394,6 +394,17 @@ class Operations(object): SQL-script-compatible block that selects into a @variable from sys.default_constraints, then exec's a separate DROP CONSTRAINT for that default. + :param mssql_drop_foreign_key: Optional boolean. When ``True``, on + Microsoft SQL Server only, first + drop a single FOREIGN KEY constraint on the column using a + SQL-script-compatible + block that selects into a @variable from + sys.foreign_keys/sys.foreign_key_columns, + then exec's a separate DROP CONSTRAINT for that default. Only + works if the column has exactly one FK constraint which refers to + it, at the moment. + + .. versionadded:: 0.6.2 """ diff --git a/docs/build/changelog.rst b/docs/build/changelog.rst index 4d5ae5ab..f960c59f 100644 --- a/docs/build/changelog.rst +++ b/docs/build/changelog.rst @@ -6,6 +6,17 @@ Changelog .. changelog:: :version: 0.6.2 + .. change:: + :tags: feature, mssql + + Added new argument ``mssql_drop_foreign_key`` to + :meth:`.Operations.drop_column`. Like ``mssql_drop_default`` + and ``mssql_drop_check``, will do an inline lookup for a + single foreign key which applies to this column, and drop it. + For a column with more than one FK, you'd still need to explicitly + use :meth:`.Operations.drop_constraint` given the name, + even though only MSSQL has this limitation in the first place. + .. change:: :tags: bug, mssql :pullreq: bitbucket:13 diff --git a/tests/test_mssql.py b/tests/test_mssql.py index c0182b2d..382c99ce 100644 --- a/tests/test_mssql.py +++ b/tests/test_mssql.py @@ -110,6 +110,12 @@ class OpTest(TestCase): "ALTER TABLE t ALTER COLUMN c INTEGER NULL" ) + def test_drop_column_w_fk(self): + context = op_fixture('mssql') + op.drop_column('t1', 'c1', mssql_drop_foreign_key=True) + context.assert_contains("exec('alter table t1 drop constraint ' + @const_name)") + context.assert_contains("ALTER TABLE t1 DROP COLUMN c1") + def test_alter_column_not_nullable_w_existing_type(self): context = op_fixture('mssql') op.alter_column("t", "c", nullable=False, existing_type=Integer)