]> git.ipfire.org Git - thirdparty/sqlalchemy/alembic.git/commitdiff
- Added/fixed support for MySQL "SET DEFAULT" / "DROP DEFAULT" phrases,
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 16 Mar 2014 23:26:21 +0000 (19:26 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 16 Mar 2014 23:26:21 +0000 (19:26 -0400)
which will now be rendered if only the server default is changing
or being dropped (e.g. specify None to alter_column() to indicate
"DROP DEFAULT").  Also added support for rendering MODIFY rather than
CHANGE when the column name isn't changing.
fixes #103

alembic/ddl/mysql.py
docs/build/changelog.rst
tests/test_mysql.py

index 69b32b2ab401b94e353d9188cf31921ee5dccbb0..3d954f6cf458bec0adfe0cf19a64028c032da595 100644 (file)
@@ -6,7 +6,8 @@ from ..compat import string_types
 from .. import util
 from .impl import DefaultImpl
 from .base import ColumnNullable, ColumnName, ColumnDefault, \
-            ColumnType, AlterColumn, format_column_name
+            ColumnType, AlterColumn, format_column_name, \
+            format_server_default
 from .base import alter_table
 
 class MySQLImpl(DefaultImpl):
@@ -26,22 +27,49 @@ class MySQLImpl(DefaultImpl):
                         existing_nullable=None,
                         existing_autoincrement=None
                     ):
-        self._exec(
-            MySQLAlterColumn(
-                table_name, column_name,
-                schema=schema,
-                newname=name if name is not None else column_name,
-                nullable=nullable if nullable is not None else
-                                existing_nullable
-                                if existing_nullable is not None
-                                else True,
-                type_=type_ if type_ is not None else existing_type,
-                default=server_default if server_default is not False
-                                            else existing_server_default,
-                autoincrement=autoincrement if autoincrement is not None
-                                            else existing_autoincrement
+        if name is not None:
+            self._exec(
+                MySQLChangeColumn(
+                    table_name, column_name,
+                    schema=schema,
+                    newname=name,
+                    nullable=nullable if nullable is not None else
+                                    existing_nullable
+                                    if existing_nullable is not None
+                                    else True,
+                    type_=type_ if type_ is not None else existing_type,
+                    default=server_default if server_default is not False
+                                                else existing_server_default,
+                    autoincrement=autoincrement if autoincrement is not None
+                                                else existing_autoincrement
+                )
+            )
+        elif nullable is not None or \
+            type_ is not None or \
+            autoincrement is not None:
+            self._exec(
+                MySQLModifyColumn(
+                    table_name, column_name,
+                    schema=schema,
+                    newname=name if name is not None else column_name,
+                    nullable=nullable if nullable is not None else
+                                    existing_nullable
+                                    if existing_nullable is not None
+                                    else True,
+                    type_=type_ if type_ is not None else existing_type,
+                    default=server_default if server_default is not False
+                                                else existing_server_default,
+                    autoincrement=autoincrement if autoincrement is not None
+                                                else existing_autoincrement
+                )
+            )
+        elif server_default is not False:
+            self._exec(
+                MySQLAlterDefault(
+                    table_name, column_name, server_default,
+                    schema=schema,
+                )
             )
-        )
 
     def correct_for_autogen_constraints(self, conn_unique_constraints, conn_indexes,
                                         metadata_unique_constraints,
@@ -53,7 +81,14 @@ class MySQLImpl(DefaultImpl):
                 conn_indexes.remove(idx)
 
 
-class MySQLAlterColumn(AlterColumn):
+class MySQLAlterDefault(AlterColumn):
+    def __init__(self, name, column_name, default, schema=None):
+        super(AlterColumn, self).__init__(name, schema=schema)
+        self.column_name = column_name
+        self.default = default
+
+
+class MySQLChangeColumn(AlterColumn):
     def __init__(self, name, column_name, schema=None,
                         newname=None,
                         type_=None,
@@ -68,12 +103,16 @@ class MySQLAlterColumn(AlterColumn):
         self.autoincrement = autoincrement
         if type_ is None:
             raise util.CommandError(
-                "All MySQL ALTER COLUMN operations "
+                "All MySQL CHANGE/MODIFY COLUMN operations "
                 "require the existing type."
             )
 
         self.type_ = sqltypes.to_instance(type_)
 
+class MySQLModifyColumn(MySQLChangeColumn):
+    pass
+
+
 @compiles(ColumnNullable, 'mysql')
 @compiles(ColumnName, 'mysql')
 @compiles(ColumnDefault, 'mysql')
@@ -84,14 +123,39 @@ def _mysql_doesnt_support_individual(element, compiler, **kw):
         )
 
 
-@compiles(MySQLAlterColumn, "mysql")
-def _mysql_alter_column(element, compiler, **kw):
-    return "%s CHANGE %s %s" % (
+@compiles(MySQLAlterDefault, "mysql")
+def _mysql_alter_default(element, compiler, **kw):
+    return "%s ALTER COLUMN %s %s" % (
+        alter_table(compiler, element.table_name, element.schema),
+        format_column_name(compiler, element.column_name),
+        "SET DEFAULT %s" % format_server_default(compiler, element.default)
+             if element.default is not None
+            else "DROP DEFAULT"
+    )
+
+@compiles(MySQLModifyColumn, "mysql")
+def _mysql_modify_column(element, compiler, **kw):
+    return "%s MODIFY %s %s" % (
+        alter_table(compiler, element.table_name, element.schema),
+        format_column_name(compiler, element.column_name),
+        _mysql_colspec(
+            compiler,
+            nullable=element.nullable,
+            server_default=element.default,
+            type_=element.type_,
+            autoincrement=element.autoincrement
+        ),
+    )
+
+
+@compiles(MySQLChangeColumn, "mysql")
+def _mysql_change_column(element, compiler, **kw):
+    return "%s CHANGE %s %s %s" % (
         alter_table(compiler, element.table_name, element.schema),
         format_column_name(compiler, element.column_name),
+        format_column_name(compiler, element.newname),
         _mysql_colspec(
             compiler,
-            name=element.newname,
             nullable=element.nullable,
             server_default=element.default,
             type_=element.type_,
@@ -105,10 +169,9 @@ def _render_value(compiler, expr):
     else:
         return compiler.sql_compiler.process(expr)
 
-def _mysql_colspec(compiler, name, nullable, server_default, type_,
+def _mysql_colspec(compiler, nullable, server_default, type_,
                                         autoincrement):
-    spec = "%s %s %s" % (
-        format_column_name(compiler, name),
+    spec = "%s %s" % (
         compiler.dialect.type_compiler.process(type_),
         "NULL" if nullable else "NOT NULL"
     )
index 8fae97e2b39fcd3c06ed09c3ca548c1294bfe1ac..8ba2855fe3ad9c5858c05c2de5cf2dd7b11c04b6 100644 (file)
@@ -5,6 +5,16 @@ Changelog
 .. changelog::
     :version: 0.6.4
 
+    .. change::
+      :tags: bug, mysql
+      :tickets: 103
+
+      Added/fixed support for MySQL "SET DEFAULT" / "DROP DEFAULT" phrases,
+      which will now be rendered if only the server default is changing
+      or being dropped (e.g. specify None to alter_column() to indicate
+      "DROP DEFAULT").  Also added support for rendering MODIFY rather than
+      CHANGE when the column name isn't changing.
+
     .. change::
       :tags: bug
       :tickets: 190
index 58686901560d44e2741bd6c4c4dcb3d13a936e6d..f598d997a7dd62f5329fbb8070df3e483579de2d 100644 (file)
@@ -41,12 +41,12 @@ class MySQLOpTest(TestCase):
 
     def test_rename_column_serv_compiled_default(self):
         context = op_fixture('mysql')
-        op.alter_column('t1', 'c1', new_column_name="c2", existing_type=Integer,
-                existing_server_default=func.utc_thing(func.current_timestamp()))
+        op.alter_column('t1', 'c1', existing_type=Integer,
+                server_default=func.utc_thing(func.current_timestamp()))
         # this is not a valid MySQL default but the point is to just
         # test SQL expression rendering
         context.assert_(
-            "ALTER TABLE t1 CHANGE c1 c2 INTEGER NULL DEFAULT utc_thing(CURRENT_TIMESTAMP)"
+            "ALTER TABLE t1 ALTER COLUMN c1 SET DEFAULT utc_thing(CURRENT_TIMESTAMP)"
         )
 
     def test_rename_column_autoincrement(self):
@@ -59,41 +59,79 @@ class MySQLOpTest(TestCase):
 
     def test_col_add_autoincrement(self):
         context = op_fixture('mysql')
-        op.alter_column('t1', 'c1', new_column_name="c2", existing_type=Integer,
+        op.alter_column('t1', 'c1', existing_type=Integer,
                                     autoincrement=True)
         context.assert_(
-            'ALTER TABLE t1 CHANGE c1 c2 INTEGER NULL AUTO_INCREMENT'
+            'ALTER TABLE t1 MODIFY c1 INTEGER NULL AUTO_INCREMENT'
         )
 
     def test_col_remove_autoincrement(self):
         context = op_fixture('mysql')
-        op.alter_column('t1', 'c1', new_column_name="c2", existing_type=Integer,
+        op.alter_column('t1', 'c1', existing_type=Integer,
                                     existing_autoincrement=True,
                                     autoincrement=False)
         context.assert_(
-            'ALTER TABLE t1 CHANGE c1 c2 INTEGER NULL'
+            'ALTER TABLE t1 MODIFY c1 INTEGER NULL'
         )
 
-    def test_col_nullable(self):
+
+    def test_col_dont_remove_server_default(self):
+        context = op_fixture('mysql')
+        op.alter_column('t1', 'c1', existing_type=Integer,
+                                    existing_server_default='1',
+                                    server_default=False)
+
+        context.assert_()
+
+    def test_alter_column_drop_default(self):
+        context = op_fixture('mysql')
+        op.alter_column("t", "c", existing_type=Integer, server_default=None)
+        context.assert_(
+            'ALTER TABLE t ALTER COLUMN c DROP DEFAULT'
+        )
+
+    def test_alter_column_modify_default(self):
+        context = op_fixture('mysql')
+        # notice we dont need the existing type on this one...
+        op.alter_column("t", "c", server_default='1')
+        context.assert_(
+            "ALTER TABLE t ALTER COLUMN c SET DEFAULT '1'"
+        )
+
+    def test_col_not_nullable(self):
         context = op_fixture('mysql')
         op.alter_column('t1', 'c1', nullable=False, existing_type=Integer)
         context.assert_(
-            'ALTER TABLE t1 CHANGE c1 c1 INTEGER NOT NULL'
+            'ALTER TABLE t1 MODIFY c1 INTEGER NOT NULL'
+        )
+
+    def test_col_not_nullable_existing_serv_default(self):
+        context = op_fixture('mysql')
+        op.alter_column('t1', 'c1', nullable=False, existing_type=Integer,
+                                    existing_server_default='5')
+        context.assert_(
+            "ALTER TABLE t1 MODIFY c1 INTEGER NOT NULL DEFAULT '5'"
+        )
+
+    def test_col_nullable(self):
+        context = op_fixture('mysql')
+        op.alter_column('t1', 'c1', nullable=True, existing_type=Integer)
+        context.assert_(
+            'ALTER TABLE t1 MODIFY c1 INTEGER NULL'
         )
 
     def test_col_multi_alter(self):
         context = op_fixture('mysql')
         op.alter_column('t1', 'c1', nullable=False, server_default="q", type_=Integer)
         context.assert_(
-            "ALTER TABLE t1 CHANGE c1 c1 INTEGER NOT NULL DEFAULT 'q'"
+            "ALTER TABLE t1 MODIFY c1 INTEGER NOT NULL DEFAULT 'q'"
         )
 
-
     def test_col_alter_type_required(self):
         op_fixture('mysql')
         assert_raises_message(
             util.CommandError,
-            "All MySQL ALTER COLUMN operations require the existing type.",
+            "MySQL CHANGE/MODIFY COLUMN operations require the existing type.",
             op.alter_column, 't1', 'c1', nullable=False, server_default="q"
         )