summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2012-04-30 15:17:18 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2012-04-30 15:17:18 -0400
commit17b7299729fe1ff05e1638d555233875bc21caab (patch)
treef6ffbcbac40b280ec2ee691258ac156b7d67d891
parent22c36646b82ab1c4d076fc52e4c7e741ca56567c (diff)
downloadalembic-rel_0_3_2.tar.gz
- [feature] Basic support for Oracle added,rel_0_3_2
courtesy shgoh. #40
-rw-r--r--CHANGES3
-rw-r--r--alembic/ddl/__init__.py2
-rw-r--r--alembic/ddl/impl.py7
-rw-r--r--alembic/ddl/mssql.py2
-rw-r--r--alembic/ddl/oracle.py77
-rw-r--r--alembic/environment.py16
-rw-r--r--tests/test_oracle.py156
7 files changed, 251 insertions, 12 deletions
diff --git a/CHANGES b/CHANGES
index 241c57a..40a5765 100644
--- 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
diff --git a/alembic/ddl/__init__.py b/alembic/ddl/__init__.py
index 128b14c..e252fae 100644
--- a/alembic/ddl/__init__.py
+++ b/alembic/ddl/__init__.py
@@ -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
diff --git a/alembic/ddl/impl.py b/alembic/ddl/impl.py
index 0c5d794..95ead4c 100644
--- a/alembic/ddl/impl.py
+++ b/alembic/ddl/impl.py
@@ -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
diff --git a/alembic/ddl/mssql.py b/alembic/ddl/mssql.py
index 2f8c9eb..42e8447 100644
--- a/alembic/ddl/mssql.py
+++ b/alembic/ddl/mssql.py
@@ -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
index 0000000..bda9578
--- /dev/null
+++ b/alembic/ddl/oracle.py
@@ -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)
diff --git a/alembic/environment.py b/alembic/environment.py
index 52c353a..ab7a7d4 100644
--- a/alembic/environment.py
+++ b/alembic/environment.py
@@ -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
index 0000000..a14197d
--- /dev/null
+++ b/tests/test_oracle.py
@@ -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'
+ # )
+