]> git.ipfire.org Git - thirdparty/sqlalchemy/alembic.git/commitdiff
- [feature] Basic support for Oracle added, rel_0_3_2
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 30 Apr 2012 19:17:18 +0000 (15:17 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 30 Apr 2012 19:17:18 +0000 (15:17 -0400)
  courtesy shgoh. #40

CHANGES
alembic/ddl/__init__.py
alembic/ddl/impl.py
alembic/ddl/mssql.py
alembic/ddl/oracle.py [new file with mode: 0644]
alembic/environment.py
tests/test_oracle.py [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index 241c57ac88e678d1f89ce27aa33be919c4ba2929..40a57654134dee7436e647a5eb58c587ff85839b 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,8 @@
 0.3.2
 =====
+- [feature] Basic support for Oracle added, 
+  courtesy shgoh. #40
+
 - [feature] Added support for UniqueConstraint
   in autogenerate, courtesy Atsushi Odagiri
 
index 128b14cd33c20473c671794ffacfeaaef5ab801c..e252fae4ca83bb5f063aab8767ae903b2cb8c3fc 100644 (file)
@@ -1,2 +1,2 @@
-import postgresql, mysql, sqlite, mssql
+import postgresql, mysql, sqlite, mssql, oracle
 from impl import DefaultImpl
\ No newline at end of file
index 0c5d794e8f3ceae136237d7e20f42354a17b1eaf..95ead4c5b054ea9e56c3f04f5b0f3c10d4b5d95c 100644 (file)
@@ -31,6 +31,7 @@ class DefaultImpl(object):
     __dialect__ = 'default'
 
     transactional_ddl = False
+    command_terminator = ";"
 
     def __init__(self, dialect, connection, as_sql, 
                     transactional_ddl, output_buffer,
@@ -66,7 +67,7 @@ class DefaultImpl(object):
                 raise Exception("Execution arguments not allowed with as_sql")
             self.static_output(unicode(
                     construct.compile(dialect=self.dialect)
-                    ).replace("\t", "    ").strip() + ";")
+                    ).replace("\t", "    ").strip() + self.command_terminator)
         else:
             conn = self.connection
             if execution_options:
@@ -217,7 +218,7 @@ class DefaultImpl(object):
         via :meth:`.EnvironmentContext.begin_transaction`.
         
         """
-        self.static_output("BEGIN;")
+        self.static_output("BEGIN" + self.command_terminator)
 
     def emit_commit(self):
         """Emit the string ``COMMIT``, or the backend-specific
@@ -227,7 +228,7 @@ class DefaultImpl(object):
         via :meth:`.EnvironmentContext.begin_transaction`.
         
         """
-        self.static_output("COMMIT;")
+        self.static_output("COMMIT" + self.command_terminator)
 
 class _literal_bindparam(_BindParamClause):
     pass
index 2f8c9ebc6cdb9e967ea47120456b1f167d325430..42e844787e5ea2477b8dfeadd7a9ed5c7008ed36 100644 (file)
@@ -22,7 +22,7 @@ class MSSQLImpl(DefaultImpl):
             self.static_output(self.batch_separator)
 
     def emit_begin(self):
-        self.static_output("BEGIN TRANSACTION;")
+        self.static_output("BEGIN TRANSACTION" + self.command_terminator)
 
     def alter_column(self, table_name, column_name, 
                         nullable=None,
diff --git a/alembic/ddl/oracle.py b/alembic/ddl/oracle.py
new file mode 100644 (file)
index 0000000..bda9578
--- /dev/null
@@ -0,0 +1,77 @@
+from alembic.ddl.impl import DefaultImpl
+from alembic.ddl.base import alter_table, AddColumn, ColumnName, \
+    format_table_name, format_column_name, ColumnNullable, \
+    format_server_default,ColumnDefault, format_type, ColumnType
+from alembic import util
+from sqlalchemy.ext.compiler import compiles
+
+class OracleImpl(DefaultImpl):
+    __dialect__ = 'oracle'
+    transactional_ddl = True
+    batch_separator = "/"
+    command_terminator = ""
+
+    def __init__(self, *arg, **kw):
+        super(OracleImpl, self).__init__(*arg, **kw)
+        self.batch_separator = self.context_opts.get(
+                                "oracle_batch_separator", 
+                                self.batch_separator)
+
+    def _exec(self, construct, *args, **kw):
+        super(OracleImpl, self)._exec(construct, *args, **kw)
+        if self.as_sql and self.batch_separator:
+            self.static_output(self.batch_separator)
+
+    def emit_begin(self):
+        self._exec("SET TRANSACTION READ WRITE")
+
+    def emit_commit(self):
+        self._exec("COMMIT")
+
+@compiles(AddColumn, 'oracle')
+def visit_add_column(element, compiler, **kw):
+    return "%s %s" % (
+        alter_table(compiler, element.table_name, element.schema),
+        add_column(compiler, element.column, **kw),
+    )
+
+@compiles(ColumnNullable, 'oracle')
+def visit_column_nullable(element, compiler, **kw):
+    return "%s %s %s" % (
+        alter_table(compiler, element.table_name, element.schema),
+        alter_column(compiler, element.column_name),
+        "NULL" if element.nullable else "NOT NULL"
+    )
+
+@compiles(ColumnType, 'oracle')
+def visit_column_type(element, compiler, **kw):
+    return "%s %s %s" % (
+        alter_table(compiler, element.table_name, element.schema),
+        alter_column(compiler, element.column_name),
+        "%s" % format_type(compiler, element.type_)
+    )
+
+@compiles(ColumnName, 'oracle')
+def visit_column_name(element, compiler, **kw):
+    return "%s RENAME COLUMN %s TO %s" % (
+        alter_table(compiler, element.table_name, element.schema),
+        format_column_name(compiler, element.column_name),
+        format_column_name(compiler, element.newname)
+    )
+
+@compiles(ColumnDefault, 'oracle')
+def visit_column_default(element, compiler, **kw):
+    return "%s %s %s" % (
+        alter_table(compiler, element.table_name, element.schema),
+        alter_column(compiler, element.column_name),
+        "DEFAULT %s" % 
+            format_server_default(compiler, element.default)
+        if element.default is not None
+        else "DEFAULT NULL"
+    )
+
+def alter_column(compiler, name):
+    return 'MODIFY %s' % format_column_name(compiler, name)
+
+def add_column(compiler, column, **kw):
+    return "ADD %s" % compiler.get_column_specification(column, **kw)
index 52c353a86a360a53f0ebb21fb87130ec5d8fa618..ab7a7d4ec694d0b6846a9f2eae5dada642fca1a9 100644 (file)
@@ -373,14 +373,16 @@ class EnvironmentContext(object):
         Parameters specific to individual backends:
     
         :param mssql_batch_separator: The "batch separator" which will 
-         be placed
-         between each statement when generating offline SQL Server 
+         be placed between each statement when generating offline SQL Server 
          migrations.  Defaults to ``GO``.  Note this is in addition to the 
-         customary
-         semicolon ``;`` at the end of each statement; SQL Server considers
-         the "batch separator" to denote the end of an individual statement
-         execution, and cannot group certain dependent operations in 
-         one step.
+         customary semicolon ``;`` at the end of each statement; SQL Server 
+         considers the "batch separator" to denote the end of an 
+         individual statement execution, and cannot group certain 
+         dependent operations in one step.
+        :param oracle_batch_separator: The "batch separator" which will
+         be placed between each statement when generating offline 
+         Oracle migrations.  Defaults to ``/``.  Oracle doesn't add a
+         semicolon between statements like most other backends.
 
         """
         opts = self.context_opts
diff --git a/tests/test_oracle.py b/tests/test_oracle.py
new file mode 100644 (file)
index 0000000..a14197d
--- /dev/null
@@ -0,0 +1,156 @@
+"""Test op functions against ORACLE."""
+from __future__ import with_statement
+from tests import op_fixture, capture_context_buffer, \
+    _no_sql_testing_config, assert_raises_message, staging_env, \
+    three_rev_fixture, clear_staging_env
+from alembic import op, command, util
+from sqlalchemy import Integer, Column, ForeignKey, \
+            UniqueConstraint, Table, MetaData, String
+from sqlalchemy.sql import table
+from unittest import TestCase
+
+
+class FullEnvironmentTests(TestCase):
+    @classmethod
+    def setup_class(cls):
+        env = staging_env()
+        cls.cfg = cfg = _no_sql_testing_config("oracle")
+
+        cls.a, cls.b, cls.c = \
+            three_rev_fixture(cfg)
+
+    @classmethod
+    def teardown_class(cls):
+        clear_staging_env()
+
+    def test_begin_comit(self):
+        with capture_context_buffer(transactional_ddl=True) as buf:
+            command.upgrade(self.cfg, self.a, sql=True)
+        assert "SET TRANSACTION READ WRITE\n\n/" in buf.getvalue()
+        assert "COMMIT\n\n/" in buf.getvalue()
+
+    def test_batch_separator_default(self):
+        with capture_context_buffer() as buf:
+            command.upgrade(self.cfg, self.a, sql=True)
+        assert "/" in buf.getvalue()
+        assert ";" not in buf.getvalue()
+
+    def test_batch_separator_custom(self):
+        with capture_context_buffer(oracle_batch_separator="BYE") as buf:
+            command.upgrade(self.cfg, self.a, sql=True)
+        assert "BYE" in buf.getvalue()
+
+class OpTest(TestCase):
+    def test_add_column(self):
+        context = op_fixture('oracle')
+        op.add_column('t1', Column('c1', Integer, nullable=False))
+        context.assert_("ALTER TABLE t1 ADD c1 INTEGER NOT NULL")
+
+
+    def test_add_column_with_default(self):
+        context = op_fixture("oracle")
+        op.add_column('t1', Column('c1', Integer, nullable=False, server_default="12"))
+        context.assert_("ALTER TABLE t1 ADD c1 INTEGER DEFAULT '12' NOT NULL")
+
+    def test_alter_column_rename_oracle(self):
+        context = op_fixture('oracle')
+        op.alter_column("t", "c", name="x")
+        context.assert_(
+            "ALTER TABLE t RENAME COLUMN c TO x"
+        )
+
+    def test_alter_column_new_type(self):
+        context = op_fixture('oracle')
+        op.alter_column("t", "c", type_=Integer)
+        context.assert_(
+            'ALTER TABLE t MODIFY c INTEGER'
+        )
+
+    def test_drop_index(self):
+        context = op_fixture('oracle')
+        op.drop_index('my_idx', 'my_table')
+        context.assert_contains("DROP INDEX my_idx")
+
+    def test_drop_column_w_default(self):
+        context = op_fixture('oracle')
+        op.drop_column('t1', 'c1')
+        context.assert_(
+            "ALTER TABLE t1 DROP COLUMN c1"
+        )
+
+    def test_drop_column_w_check(self):
+        context = op_fixture('oracle')
+        op.drop_column('t1', 'c1')
+        context.assert_(
+            "ALTER TABLE t1 DROP COLUMN c1"
+        )
+
+    def test_alter_column_nullable_w_existing_type(self):
+        context = op_fixture('oracle')
+        op.alter_column("t", "c", nullable=True, existing_type=Integer)
+        context.assert_(
+            "ALTER TABLE t MODIFY c NULL"
+        )
+
+    def test_alter_column_not_nullable_w_existing_type(self):
+        context = op_fixture('oracle')
+        op.alter_column("t", "c", nullable=False, existing_type=Integer)
+        context.assert_(
+            "ALTER TABLE t MODIFY c NOT NULL"
+        )
+
+    def test_alter_column_nullable_w_new_type(self):
+        context = op_fixture('oracle')
+        op.alter_column("t", "c", nullable=True, type_=Integer)
+        context.assert_(
+            "ALTER TABLE t MODIFY c NULL",
+            'ALTER TABLE t MODIFY c INTEGER'
+        )
+
+    def test_alter_column_not_nullable_w_new_type(self):
+        context = op_fixture('oracle')
+        op.alter_column("t", "c", nullable=False, type_=Integer)
+        context.assert_(
+            "ALTER TABLE t MODIFY c NOT NULL",
+            "ALTER TABLE t MODIFY c INTEGER"
+        )
+
+    def test_alter_add_server_default(self):
+        context = op_fixture('oracle')
+        op.alter_column("t", "c", server_default="5")
+        context.assert_(
+            "ALTER TABLE t MODIFY c DEFAULT '5'"
+        )
+
+    def test_alter_replace_server_default(self):
+        context = op_fixture('oracle')
+        op.alter_column("t", "c", server_default="5", existing_server_default="6")
+        context.assert_(
+            "ALTER TABLE t MODIFY c DEFAULT '5'"
+        )
+
+    def test_alter_remove_server_default(self):
+        context = op_fixture('oracle')
+        op.alter_column("t", "c", server_default=None)
+        context.assert_(
+            "ALTER TABLE t MODIFY c DEFAULT NULL"
+        )
+
+    def test_alter_do_everything(self):
+        context = op_fixture('oracle')
+        op.alter_column("t", "c", name="c2", nullable=True, type_=Integer, server_default="5")
+        context.assert_(
+            'ALTER TABLE t MODIFY c NULL',
+            "ALTER TABLE t MODIFY c DEFAULT '5'",
+            'ALTER TABLE t MODIFY c INTEGER',
+            'ALTER TABLE t RENAME COLUMN c TO c2'
+        )
+
+    # TODO: when we add schema support
+    #def test_alter_column_rename_oracle_schema(self):
+    #    context = op_fixture('oracle')
+    #    op.alter_column("t", "c", name="x", schema="y")
+    #    context.assert_(
+    #        'ALTER TABLE y.t RENAME COLUMN c TO c2'
+    #    )
+