]> git.ipfire.org Git - thirdparty/sqlalchemy/alembic.git/commitdiff
Adjust mssql accommodate existing_type and type
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 4 Mar 2021 15:54:03 +0000 (10:54 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 4 Mar 2021 15:54:03 +0000 (10:54 -0500)
Fixed bug where the "existing_type" parameter, which the MSSQL dialect
requires in order to change the nullability of a column in the absence of
also changing the column type, would cause an ALTER COLUMN operation to
incorrectly render a second ALTER statement without the nullability if a
new type were also present, as the MSSQL-specific contract did not
anticipate all three of "nullability", "type_" and "existing_type" being
sent at the same time.

Change-Id: Ia95b6c3e9276cc067fd773928f9678ab429d5670
Fixes: #812
alembic/ddl/mssql.py
docs/build/unreleased/812.rst [new file with mode: 0644]
tests/test_mssql.py
tests/test_op.py

index 18e09f8238a463dcab3ca8629f952803f358dc7b..8a99ee6e869b0270fdbb35132e238d49fc7d43fd 100644 (file)
@@ -75,18 +75,24 @@ class MSSQLImpl(DefaultImpl):
         **kw
     ):
 
-        if nullable is not None and existing_type is None:
-            if type_ is not None:
-                existing_type = type_
+        if nullable is not None:
+            if existing_type is None:
+                if type_ is not None:
+                    existing_type = type_
+                    # the NULL/NOT NULL alter will handle
+                    # the type alteration
+                    type_ = None
+                else:
+                    raise util.CommandError(
+                        "MS-SQL ALTER COLUMN operations "
+                        "with NULL or NOT NULL require the "
+                        "existing_type or a new type_ be passed."
+                    )
+            elif type_ is not None:
                 # the NULL/NOT NULL alter will handle
                 # the type alteration
+                existing_type = type_
                 type_ = None
-            else:
-                raise util.CommandError(
-                    "MS-SQL ALTER COLUMN operations "
-                    "with NULL or NOT NULL require the "
-                    "existing_type or a new type_ be passed."
-                )
 
         used_default = False
         if sqla_compat._server_default_is_identity(
diff --git a/docs/build/unreleased/812.rst b/docs/build/unreleased/812.rst
new file mode 100644 (file)
index 0000000..d4c8032
--- /dev/null
@@ -0,0 +1,12 @@
+.. change::
+    :tags: bug, mssql, operations
+    :tickets: 812
+
+    Fixed bug where the "existing_type" parameter, which the MSSQL dialect
+    requires in order to change the nullability of a column in the absence of
+    also changing the column type, would cause an ALTER COLUMN operation to
+    incorrectly render a second ALTER statement without the nullability if a
+    new type were also present, as the MSSQL-specific contract did not
+    anticipate all three of "nullability", "type_" and "existing_type" being
+    sent at the same time.
+
index 0c8b3ae63a88d3494c8585286a83482a046660bb..e9a74931a3913704a425f15cc9edc5d0a3b3bf9d 100644 (file)
@@ -3,6 +3,7 @@
 from sqlalchemy import Column
 from sqlalchemy import exc
 from sqlalchemy import Integer
+from sqlalchemy import String
 
 from alembic import command
 from alembic import op
@@ -79,10 +80,26 @@ class OpTest(TestBase):
         op.alter_column("t", "c", new_column_name="SomeFancyName")
         context.assert_("EXEC sp_rename 't.c', [SomeFancyName], 'COLUMN'")
 
-    def test_alter_column_new_type(self):
+    @combinations((True,), (False,), argnames="pass_existing_type")
+    @combinations((True,), (False,), argnames="change_nullability")
+    def test_alter_column_type_and_nullability(
+        self, pass_existing_type, change_nullability
+    ):
         context = op_fixture("mssql")
-        op.alter_column("t", "c", type_=Integer)
-        context.assert_("ALTER TABLE t ALTER COLUMN c INTEGER")
+
+        args = dict(type_=Integer)
+        if pass_existing_type:
+            args["existing_type"] = String(15)
+
+        if change_nullability:
+            args["nullable"] = False
+
+        op.alter_column("t", "c", **args)
+
+        if change_nullability:
+            context.assert_("ALTER TABLE t ALTER COLUMN c INTEGER NOT NULL")
+        else:
+            context.assert_("ALTER TABLE t ALTER COLUMN c INTEGER")
 
     def test_alter_column_dont_touch_constraints(self):
         context = op_fixture("mssql")
index 58a6a860a728b645face22e2dbe907bc676fe9b9..93bba287e305958941b354f46c2f3464c97b8765 100644 (file)
@@ -462,6 +462,31 @@ class OpTest(TestBase):
             "ALTER TABLE foo.t ADD CONSTRAINT xyz CHECK (c IN (0, 1))",
         )
 
+    @combinations((True,), (False,), argnames="pass_existing_type")
+    @combinations((True,), (False,), argnames="change_nullability")
+    def test_generic_alter_column_type_and_nullability(
+        self, pass_existing_type, change_nullability
+    ):
+        # this test is also on the mssql dialect in test_mssql
+        context = op_fixture()
+
+        args = dict(type_=Integer)
+        if pass_existing_type:
+            args["existing_type"] = String(15)
+
+        if change_nullability:
+            args["nullable"] = False
+
+        op.alter_column("t", "c", **args)
+
+        if change_nullability:
+            context.assert_(
+                "ALTER TABLE t ALTER COLUMN c SET NOT NULL",
+                "ALTER TABLE t ALTER COLUMN c TYPE INTEGER",
+            )
+        else:
+            context.assert_("ALTER TABLE t ALTER COLUMN c TYPE INTEGER")
+
     def test_alter_column_schema_type_existing_type(self):
         context = op_fixture("mssql", native_boolean=False)
         op.alter_column(