summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-03-16 19:26:21 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2014-03-16 19:26:21 -0400
commitb61eccce5e45651ccdb25e61dbfce6b7af0ebe89 (patch)
treed07ae8776e0f7b3a6d1975bf4e59ce64580ac801
parenta9426a4ec4f14aa12c17f22505771a484c87c304 (diff)
downloadalembic-b61eccce5e45651ccdb25e61dbfce6b7af0ebe89.tar.gz
- 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. fixes #103
-rw-r--r--alembic/ddl/mysql.py113
-rw-r--r--docs/build/changelog.rst10
-rw-r--r--tests/test_mysql.py62
3 files changed, 148 insertions, 37 deletions
diff --git a/alembic/ddl/mysql.py b/alembic/ddl/mysql.py
index 69b32b2..3d954f6 100644
--- a/alembic/ddl/mysql.py
+++ b/alembic/ddl/mysql.py
@@ -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"
)
diff --git a/docs/build/changelog.rst b/docs/build/changelog.rst
index 8fae97e..8ba2855 100644
--- a/docs/build/changelog.rst
+++ b/docs/build/changelog.rst
@@ -6,6 +6,16 @@ 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
diff --git a/tests/test_mysql.py b/tests/test_mysql.py
index 5868690..f598d99 100644
--- a/tests/test_mysql.py
+++ b/tests/test_mysql.py
@@ -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"
)