**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(
--- /dev/null
+.. 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.
+
from sqlalchemy import Column
from sqlalchemy import exc
from sqlalchemy import Integer
+from sqlalchemy import String
from alembic import command
from alembic import op
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")
"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(