]> git.ipfire.org Git - thirdparty/sqlalchemy/alembic.git/commitdiff
Support USING for Postgresql ALTER COLUMN.
authorFrazer McLean <frazer@frazermclean.co.uk>
Fri, 17 Jun 2016 16:55:11 +0000 (12:55 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 22 Aug 2016 21:46:42 +0000 (17:46 -0400)
Added support for the USING clause to the ALTER COLUMN operation
for Postgresql.  Support is via the
:paramref:`.op.alter_column.postgresql_using`
parameter.

Fixes: #292
Change-Id: I8b1d418df0b6b731a68614dbffd7a9fb13de4de5
Pull-request: https://github.com/zzzeek/alembic/pull/27

alembic/ddl/postgresql.py
alembic/operations/ops.py
docs/build/changelog.rst
tests/test_postgresql.py

index dd76639f8c1f29f6cc57c37053d91827182dac33..e8d8e2fbdbeb18d9041758305f3d3351ea7c752f 100644 (file)
@@ -2,10 +2,12 @@ import re
 
 from ..util import compat
 from .. import util
-from .base import compiles, alter_table, format_table_name, RenameTable
+from .base import compiles, alter_column, alter_table, format_table_name, \
+    format_type, AlterColumn, RenameTable
 from .impl import DefaultImpl
 from sqlalchemy.dialects.postgresql import INTEGER, BIGINT
 from sqlalchemy import text, Numeric, Column
+from sqlalchemy import types as sqltypes
 
 if compat.sqla_08:
     from sqlalchemy.sql.expression import UnaryExpression
@@ -61,6 +63,49 @@ class PostgresqlImpl(DefaultImpl):
             )
         )
 
+    def alter_column(self, table_name, column_name,
+                     nullable=None,
+                     server_default=False,
+                     name=None,
+                     type_=None,
+                     schema=None,
+                     autoincrement=None,
+                     existing_type=None,
+                     existing_server_default=None,
+                     existing_nullable=None,
+                     existing_autoincrement=None,
+                     **kw
+                     ):
+
+        using = kw.pop('postgresql_using', None)
+
+        if using is not None and type_ is None:
+            raise util.CommandError(
+                "postgresql_using must be used with the type_ parameter")
+
+        if type_ is not None:
+            self._exec(PostgresqlColumnType(
+                table_name, column_name, type_, schema=schema,
+                using=using, existing_type=existing_type,
+                existing_server_default=existing_server_default,
+                existing_nullable=existing_nullable,
+            ))
+
+        super(PostgresqlImpl, self).alter_column(
+            table_name, column_name,
+            nullable=nullable,
+            server_default=server_default,
+            name=name,
+            schema=schema,
+            autoincrement=autoincrement,
+            existing_type=existing_type,
+            existing_server_default=existing_server_default,
+            existing_nullable=existing_nullable,
+            existing_autoincrement=existing_autoincrement,
+            **kw)
+
+
+
     def autogen_column_reflect(self, inspector, table, column_info):
         if column_info.get('default') and \
                 isinstance(column_info['type'], (INTEGER, BIGINT)):
@@ -125,9 +170,28 @@ class PostgresqlImpl(DefaultImpl):
                     metadata_indexes.discard(idx)
 
 
+class PostgresqlColumnType(AlterColumn):
+
+    def __init__(self, name, column_name, type_, **kw):
+        using = kw.pop('using', None)
+        super(PostgresqlColumnType, self).__init__(name, column_name, **kw)
+        self.type_ = sqltypes.to_instance(type_)
+        self.using = using
+
+
 @compiles(RenameTable, "postgresql")
 def visit_rename_table(element, compiler, **kw):
     return "%s RENAME TO %s" % (
         alter_table(compiler, element.table_name, element.schema),
         format_table_name(compiler, element.new_table_name, None)
     )
+
+
+@compiles(PostgresqlColumnType, "postgresql")
+def visit_column_type(element, compiler, **kw):
+    return "%s %s %s %s" % (
+        alter_table(compiler, element.table_name, element.schema),
+        alter_column(compiler, element.column_name),
+        "TYPE %s" % format_type(compiler, element.type_),
+        "USING %s" % element.using if element.using else ""
+    )
index dfa17ccc5709d3f9970f339d7ec4028bff7b97d2..f892227c94ec61ebf4dffaf96c4ef3d38d973709 100644 (file)
@@ -1397,6 +1397,12 @@ class AlterColumnOp(AlterTableOp):
          .. versionadded:: 0.7.0 'schema' can now accept a
             :class:`~sqlalchemy.sql.elements.quoted_name` construct.
 
+        :param postgresql_using: String argument which will indicate a
+         SQL expression to render within the Postgresql-specific USING clause
+         within ALTER COLUMN.
+
+         .. versionadded:: 0.8.8
+
         """
 
         alt = cls(
index 118e39e025480f9847e8a40e24ee080ccfa5e447..ebd54fdace9daec96d4866443a4bd48c167404f4 100644 (file)
@@ -6,6 +6,15 @@ Changelog
 .. changelog::
     :version: 0.8.8
 
+    .. change::
+      :tags: feature, operations, postgresql
+      :tickets: 292
+
+      Added support for the USING clause to the ALTER COLUMN operation
+      for Postgresql.  Support is via the
+      :paramref:`.op.alter_column.postgresql_using`
+      parameter.  Pull request courtesy Frazer McLean.
+
 .. changelog::
     :version: 0.8.7
     :released: July 26, 2016
index 1724898f2dae7cb2264a915296a535d4bb957f0a..d39df72bd368259c90c23ea4492d15d73a568be1 100644 (file)
@@ -59,6 +59,13 @@ class PostgresqlOpTest(TestBase):
             "CREATE INDEX geocoded ON locations (coordinates) "
             "WHERE locations.coordinates != Null")
 
+    def test_alter_column_type_using(self):
+        context = op_fixture('postgresql')
+        op.alter_column("t", "c", type_=Integer, postgresql_using='c::integer')
+        context.assert_(
+            'ALTER TABLE t ALTER COLUMN c TYPE INTEGER USING c::integer'
+        )
+
 
 class PGOfflineEnumTest(TestBase):
 
@@ -518,5 +525,3 @@ class PostgresqlDetectSerialTest(TestBase):
             None,
             Column('x', Integer, autoincrement=False, primary_key=True)
         )
-
-