adding basic support for firebird, fixes #55
This commit is contained in:
parent
17cc5f36e6
commit
a8c31eb25f
10
docs/api.rst
10
docs/api.rst
@ -39,6 +39,16 @@ Module :mod:`mysql <migrate.changeset.databases.mysql>`
|
|||||||
:members:
|
:members:
|
||||||
:synopsis: MySQL database specific changeset implementations
|
:synopsis: MySQL database specific changeset implementations
|
||||||
|
|
||||||
|
.. _firebird-d:
|
||||||
|
|
||||||
|
Module :mod:`firebird <migrate.changeset.databases.firebird>`
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
||||||
|
.. automodule:: migrate.changeset.databases.firebird
|
||||||
|
:members:
|
||||||
|
:synopsis: Firebird database specific changeset implementations
|
||||||
|
|
||||||
.. _oracle-d:
|
.. _oracle-d:
|
||||||
|
|
||||||
Module :mod:`oracle <migrate.changeset.databases.oracle>`
|
Module :mod:`oracle <migrate.changeset.databases.oracle>`
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
0.5.5
|
0.5.5
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
- added support for :ref:`firebird <firebird-d>`
|
||||||
- server_defaults passed to column.create are now issued correctly
|
- server_defaults passed to column.create are now issued correctly
|
||||||
- constraints passed to column.create are correctly interpreted (ALTER TABLE ADD CONSTRAINT is issued after ADD COLUMN)
|
- constraints passed to column.create are correctly interpreted (ALTER TABLE ADD CONSTRAINT is issued after ADD COLUMN)
|
||||||
- column.create accepts `primary_key_name`, `unique_name` and `index_name` as string value which is used as contraint name when adding a column
|
- column.create accepts `primary_key_name`, `unique_name` and `index_name` as string value which is used as contraint name when adding a column
|
||||||
|
@ -43,40 +43,40 @@ Download and Development
|
|||||||
Dialect support
|
Dialect support
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
|
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+-------------------------------+-------+
|
||||||
| Operation / Dialect | :ref:`sqlite <sqlite-d>` | :ref:`postgres <postgres-d>` | :ref:`mysql <mysql-d>` | :ref:`oracle <oracle-d>` | firebird | mssql |
|
| Operation / Dialect | :ref:`sqlite <sqlite-d>` | :ref:`postgres <postgres-d>` | :ref:`mysql <mysql-d>` | :ref:`oracle <oracle-d>` | :ref:`firebird <firebird-d>` | mssql |
|
||||||
| | | | | | | |
|
| | | | | | | |
|
||||||
+=========================================================+==========================+==============================+========================+===========================+==========+=======+
|
+=========================================================+==========================+==============================+========================+===========================+===============================+=======+
|
||||||
| :ref:`ALTER TABLE RENAME TABLE <table-rename>` | yes | yes | yes | yes | | |
|
| :ref:`ALTER TABLE RENAME TABLE <table-rename>` | yes | yes | yes | yes | no | |
|
||||||
| | | | | | | |
|
| | | | | | | |
|
||||||
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
|
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+-------------------------------+-------+
|
||||||
| :ref:`ALTER TABLE RENAME COLUMN <column-alter>` | yes | yes | yes | yes | | |
|
| :ref:`ALTER TABLE RENAME COLUMN <column-alter>` | yes | yes | yes | yes | yes | |
|
||||||
| | (workaround) [#1]_ | | | | | |
|
| | (workaround) [#1]_ | | | | | |
|
||||||
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
|
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+-------------------------------+-------+
|
||||||
| :ref:`ALTER TABLE ADD COLUMN <column-create>` | yes | yes | yes | yes | | |
|
| :ref:`ALTER TABLE ADD COLUMN <column-create>` | yes | yes | yes | yes | yes | |
|
||||||
| | (with limitations) [#2]_ | | | | | |
|
| | (with limitations) [#2]_ | | | | | |
|
||||||
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
|
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+-------------------------------+-------+
|
||||||
| :ref:`ALTER TABLE DROP COLUMN <column-drop>` | yes | yes | yes | yes | | |
|
| :ref:`ALTER TABLE DROP COLUMN <column-drop>` | yes | yes | yes | yes | yes | |
|
||||||
| | (workaround) [#1]_ | | | | | |
|
| | (workaround) [#1]_ | | | | | |
|
||||||
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
|
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+-------------------------------+-------+
|
||||||
| :ref:`ALTER TABLE ALTER COLUMN <column-alter>` | no | yes | yes | yes | | |
|
| :ref:`ALTER TABLE ALTER COLUMN <column-alter>` | no | yes | yes | yes | yes [#4]_ | |
|
||||||
| | | | | (with limitations) [#3]_ | | |
|
| | | | | (with limitations) [#3]_ | | |
|
||||||
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
|
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+-------------------------------+-------+
|
||||||
| :ref:`ALTER TABLE ADD CONSTRAINT <constraint-tutorial>` | no | yes | yes | yes | | |
|
| :ref:`ALTER TABLE ADD CONSTRAINT <constraint-tutorial>` | no | yes | yes | yes | yes | |
|
||||||
| | | | | | | |
|
| | | | | | | |
|
||||||
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
|
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+-------------------------------+-------+
|
||||||
| :ref:`ALTER TABLE DROP CONSTRAINT <constraint-tutorial>`| no | yes | yes | yes | | |
|
| :ref:`ALTER TABLE DROP CONSTRAINT <constraint-tutorial>`| no | yes | yes | yes | yes | |
|
||||||
| | | | | | | |
|
| | | | | | | |
|
||||||
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
|
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+-------------------------------+-------+
|
||||||
| :ref:`RENAME INDEX <index-rename>` | no | yes | no | yes | | |
|
| :ref:`RENAME INDEX <index-rename>` | no | yes | no | yes | yes | |
|
||||||
| | | | | | | |
|
| | | | | | | |
|
||||||
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
|
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+-------------------------------+-------+
|
||||||
|
|
||||||
|
|
||||||
.. [#1] Table is renamed to temporary table, new table is created followed by INSERT statements.
|
.. [#1] Table is renamed to temporary table, new table is created followed by INSERT statements.
|
||||||
.. [#2] Visit http://www.sqlite.org/lang_altertable.html for more information.
|
.. [#2] Visit http://www.sqlite.org/lang_altertable.html for more information.
|
||||||
.. [#3] You can not change datatype or rename column if table has NOT NULL data, see http://blogs.x2line.com/al/archive/2005/08/30/1231.aspx for more information.
|
.. [#3] You can not change datatype or rename column if table has NOT NULL data, see http://blogs.x2line.com/al/archive/2005/08/30/1231.aspx for more information.
|
||||||
|
.. [#4] Changing nullable is not supported
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
|
@ -189,7 +189,7 @@ class ANSISchemaChanger(AlterTableVisitor, SchemaGenerator):
|
|||||||
"""Starts ALTER COLUMN"""
|
"""Starts ALTER COLUMN"""
|
||||||
self.start_alter_table(table)
|
self.start_alter_table(table)
|
||||||
# TODO: use preparer.format_column
|
# TODO: use preparer.format_column
|
||||||
self.append("ALTER COLUMN %s " % self.preparer.quote_identifier(col_name))
|
self.append("ALTER COLUMN %s " % self.preparer.quote(col_name, table.quote))
|
||||||
|
|
||||||
def _visit_column_nullable(self, table, col_name, delta):
|
def _visit_column_nullable(self, table, col_name, delta):
|
||||||
nullable = delta['nullable']
|
nullable = delta['nullable']
|
||||||
@ -306,9 +306,12 @@ class ANSIConstraintDropper(ANSIConstraintCommon, SchemaDropper):
|
|||||||
constraint.name = self.get_constraint_name(constraint)
|
constraint.name = self.get_constraint_name(constraint)
|
||||||
self.append(self.preparer.format_constraint(constraint))
|
self.append(self.preparer.format_constraint(constraint))
|
||||||
if constraint.cascade:
|
if constraint.cascade:
|
||||||
self.append(" CASCADE")
|
self.cascade_constraint(constraint)
|
||||||
self.execute()
|
self.execute()
|
||||||
|
|
||||||
|
def cascade_constraint(self, constraint):
|
||||||
|
self.append(" CASCADE")
|
||||||
|
|
||||||
|
|
||||||
class ANSIDialect(DefaultDialect):
|
class ANSIDialect(DefaultDialect):
|
||||||
columngenerator = ANSIColumnGenerator
|
columngenerator = ANSIColumnGenerator
|
||||||
|
@ -39,6 +39,7 @@ class ConstraintChangeset(object):
|
|||||||
:keyword:`None` the instance's engine will be used
|
:keyword:`None` the instance's engine will be used
|
||||||
:type engine: :class:`sqlalchemy.engine.base.Engine`
|
:type engine: :class:`sqlalchemy.engine.base.Engine`
|
||||||
"""
|
"""
|
||||||
|
# TODO: set the parent here instead of in __init__
|
||||||
self.__do_imports('constraintgenerator', *a, **kw)
|
self.__do_imports('constraintgenerator', *a, **kw)
|
||||||
|
|
||||||
def drop(self, *a, **kw):
|
def drop(self, *a, **kw):
|
||||||
|
62
migrate/changeset/databases/firebird.py
Normal file
62
migrate/changeset/databases/firebird.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
"""
|
||||||
|
Firebird database specific implementations of changeset classes.
|
||||||
|
"""
|
||||||
|
from sqlalchemy.databases import firebird as sa_base
|
||||||
|
|
||||||
|
from migrate.changeset import ansisql, exceptions
|
||||||
|
|
||||||
|
|
||||||
|
FBSchemaGenerator = sa_base.FBSchemaGenerator
|
||||||
|
|
||||||
|
|
||||||
|
class FBColumnGenerator(FBSchemaGenerator, ansisql.ANSIColumnGenerator):
|
||||||
|
"""Firebird column generator implementation."""
|
||||||
|
|
||||||
|
|
||||||
|
class FBColumnDropper(ansisql.ANSIColumnDropper):
|
||||||
|
"""Firebird column dropper implementation."""
|
||||||
|
|
||||||
|
def visit_column(self, column):
|
||||||
|
table = self.start_alter_table(column)
|
||||||
|
self.append('DROP %s' % self.preparer.format_column(column))
|
||||||
|
self.execute()
|
||||||
|
|
||||||
|
|
||||||
|
class FBSchemaChanger(ansisql.ANSISchemaChanger):
|
||||||
|
"""Firebird schema changer implementation."""
|
||||||
|
|
||||||
|
def visit_table(self, table):
|
||||||
|
"""Rename table not supported"""
|
||||||
|
raise exceptions.NotSupportedError(
|
||||||
|
"Firebird does not support renaming tables.")
|
||||||
|
|
||||||
|
def _visit_column_name(self, table, col_name, delta):
|
||||||
|
new_name = delta['name']
|
||||||
|
self.start_alter_table(table)
|
||||||
|
self.append('ALTER COLUMN %s TO %s' % ((col_name), (new_name)))
|
||||||
|
|
||||||
|
def _visit_column_nullable(self, table, col_name, delta):
|
||||||
|
"""Changing NULL is not supported"""
|
||||||
|
# TODO: http://www.firebirdfaq.org/faq103/
|
||||||
|
raise exceptions.NotSupportedError(
|
||||||
|
"Firebird does not support altering NULL bevahior.")
|
||||||
|
|
||||||
|
|
||||||
|
class FBConstraintGenerator(ansisql.ANSIConstraintGenerator):
|
||||||
|
"""Firebird constraint generator implementation."""
|
||||||
|
|
||||||
|
|
||||||
|
class FBConstraintDropper(ansisql.ANSIConstraintDropper):
|
||||||
|
"""Firebird constaint dropper implementation."""
|
||||||
|
|
||||||
|
def cascade_constraint(self, constraint):
|
||||||
|
raise exceptions.NotSupportedError(
|
||||||
|
"Firebird does not support cascading constraints")
|
||||||
|
|
||||||
|
|
||||||
|
class FBDialect(ansisql.ANSIDialect):
|
||||||
|
columngenerator = FBColumnGenerator
|
||||||
|
columndropper = FBColumnDropper
|
||||||
|
schemachanger = FBSchemaChanger
|
||||||
|
constraintgenerator = FBConstraintGenerator
|
||||||
|
constraintdropper = FBConstraintDropper
|
@ -4,7 +4,11 @@
|
|||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from migrate.changeset import ansisql
|
from migrate.changeset import ansisql
|
||||||
from migrate.changeset.databases import sqlite, postgres, mysql, oracle
|
from migrate.changeset.databases import (sqlite,
|
||||||
|
postgres,
|
||||||
|
mysql,
|
||||||
|
oracle,
|
||||||
|
firebird)
|
||||||
|
|
||||||
|
|
||||||
# Map SA dialects to the corresponding Migrate extensions
|
# Map SA dialects to the corresponding Migrate extensions
|
||||||
@ -14,6 +18,7 @@ DIALECTS = {
|
|||||||
sa.databases.postgres.PGDialect: postgres.PGDialect,
|
sa.databases.postgres.PGDialect: postgres.PGDialect,
|
||||||
sa.databases.mysql.MySQLDialect: mysql.MySQLDialect,
|
sa.databases.mysql.MySQLDialect: mysql.MySQLDialect,
|
||||||
sa.databases.oracle.OracleDialect: oracle.OracleDialect,
|
sa.databases.oracle.OracleDialect: oracle.OracleDialect,
|
||||||
|
sa.databases.firebird.FBDialect: firebird.FBDialect,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ from sqlalchemy.util import OrderedDict
|
|||||||
|
|
||||||
__all__ = ['databases', 'operations']
|
__all__ = ['databases', 'operations']
|
||||||
|
|
||||||
databases = ('sqlite', 'postgres', 'mysql', 'oracle', 'mssql')
|
databases = ('sqlite', 'postgres', 'mysql', 'oracle', 'mssql', 'firebird')
|
||||||
|
|
||||||
# Map operation names to function names
|
# Map operation names to function names
|
||||||
operations = OrderedDict()
|
operations = OrderedDict()
|
||||||
|
@ -163,7 +163,7 @@ class ControlledSchema(object):
|
|||||||
|
|
||||||
table = Table(
|
table = Table(
|
||||||
tname, meta,
|
tname, meta,
|
||||||
Column('repository_id', String(255), primary_key=True),
|
Column('repository_id', String(250), primary_key=True),
|
||||||
Column('repository_path', Text),
|
Column('repository_path', Text),
|
||||||
Column('version', Integer), )
|
Column('version', Integer), )
|
||||||
|
|
||||||
|
@ -183,7 +183,7 @@ class TestAddDropColumn(fixture.DB):
|
|||||||
@fixture.usedb(not_supported='sqlite')
|
@fixture.usedb(not_supported='sqlite')
|
||||||
def test_pk(self):
|
def test_pk(self):
|
||||||
"""Can create columns with primary key"""
|
"""Can create columns with primary key"""
|
||||||
col = Column('data', Integer)
|
col = Column('data', Integer, nullable=False)
|
||||||
self.assertRaises(changeset.exceptions.InvalidConstraintError,
|
self.assertRaises(changeset.exceptions.InvalidConstraintError,
|
||||||
col.create, self.table, primary_key_name=True)
|
col.create, self.table, primary_key_name=True)
|
||||||
col.create(self.table, primary_key_name='data_pkey')
|
col.create(self.table, primary_key_name='data_pkey')
|
||||||
@ -192,7 +192,8 @@ class TestAddDropColumn(fixture.DB):
|
|||||||
self.table.insert(values={'data': 4}).execute()
|
self.table.insert(values={'data': 4}).execute()
|
||||||
try:
|
try:
|
||||||
self.table.insert(values={'data': 4}).execute()
|
self.table.insert(values={'data': 4}).execute()
|
||||||
except sqlalchemy.exc.IntegrityError:
|
except (sqlalchemy.exc.IntegrityError,
|
||||||
|
sqlalchemy.exc.ProgrammingError):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self.fail()
|
self.fail()
|
||||||
@ -211,7 +212,8 @@ class TestAddDropColumn(fixture.DB):
|
|||||||
self.table.insert(values={'data': 5}).execute()
|
self.table.insert(values={'data': 5}).execute()
|
||||||
try:
|
try:
|
||||||
self.table.insert(values={'data': 3}).execute()
|
self.table.insert(values={'data': 3}).execute()
|
||||||
except sqlalchemy.exc.IntegrityError:
|
except (sqlalchemy.exc.IntegrityError,
|
||||||
|
sqlalchemy.exc.ProgrammingError):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self.fail()
|
self.fail()
|
||||||
@ -230,7 +232,8 @@ class TestAddDropColumn(fixture.DB):
|
|||||||
self.table.insert(values={'data': 5}).execute()
|
self.table.insert(values={'data': 5}).execute()
|
||||||
try:
|
try:
|
||||||
self.table.insert(values={'data': 5}).execute()
|
self.table.insert(values={'data': 5}).execute()
|
||||||
except sqlalchemy.exc.IntegrityError:
|
except (sqlalchemy.exc.IntegrityError,
|
||||||
|
sqlalchemy.exc.ProgrammingError):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self.fail()
|
self.fail()
|
||||||
@ -249,11 +252,13 @@ class TestAddDropColumn(fixture.DB):
|
|||||||
self.table.insert(values={'data': 5}).execute()
|
self.table.insert(values={'data': 5}).execute()
|
||||||
try:
|
try:
|
||||||
self.table.insert(values={'data': 5}).execute()
|
self.table.insert(values={'data': 5}).execute()
|
||||||
except sqlalchemy.exc.IntegrityError:
|
except (sqlalchemy.exc.IntegrityError,
|
||||||
|
sqlalchemy.exc.ProgrammingError):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self.fail()
|
self.fail()
|
||||||
|
|
||||||
|
Index('ix_data', col).drop(bind=self.engine)
|
||||||
col.drop()
|
col.drop()
|
||||||
|
|
||||||
@fixture.usedb()
|
@fixture.usedb()
|
||||||
@ -272,6 +277,7 @@ class TestAddDropColumn(fixture.DB):
|
|||||||
# TODO: test that if column is appended on creation and removed on deletion
|
# TODO: test that if column is appended on creation and removed on deletion
|
||||||
# TODO: test column.alter with all changes at one time
|
# TODO: test column.alter with all changes at one time
|
||||||
# TODO: test quoting
|
# TODO: test quoting
|
||||||
|
# TODO: test drop default
|
||||||
|
|
||||||
|
|
||||||
class TestRename(fixture.DB):
|
class TestRename(fixture.DB):
|
||||||
@ -283,7 +289,7 @@ class TestRename(fixture.DB):
|
|||||||
super(TestRename, self)._setup(url)
|
super(TestRename, self)._setup(url)
|
||||||
self.meta.bind = self.engine
|
self.meta.bind = self.engine
|
||||||
|
|
||||||
@fixture.usedb()
|
@fixture.usedb(not_supported='firebird')
|
||||||
def test_rename_table(self):
|
def test_rename_table(self):
|
||||||
"""Tables can be renamed"""
|
"""Tables can be renamed"""
|
||||||
c_name = 'col_1'
|
c_name = 'col_1'
|
||||||
@ -463,17 +469,25 @@ class TestColumnChange(fixture.DB):
|
|||||||
self.table.c.data.alter(Column('data', String(42)))
|
self.table.c.data.alter(Column('data', String(42)))
|
||||||
self.refresh_table(self.table.name)
|
self.refresh_table(self.table.name)
|
||||||
self.assert_(isinstance(self.table.c.data.type, String))
|
self.assert_(isinstance(self.table.c.data.type, String))
|
||||||
|
if self.engine.name == 'firebird':
|
||||||
|
self.assertEquals(self.table.c.data.type.length, 42 * 4)
|
||||||
|
else:
|
||||||
self.assertEquals(self.table.c.data.type.length, 42)
|
self.assertEquals(self.table.c.data.type.length, 42)
|
||||||
|
|
||||||
# Just the new type
|
# Just the new type
|
||||||
self.table.c.data.alter(type=String(21))
|
self.table.c.data.alter(type=String(43))
|
||||||
self.refresh_table(self.table.name)
|
self.refresh_table(self.table.name)
|
||||||
self.assert_(isinstance(self.table.c.data.type, String))
|
self.assert_(isinstance(self.table.c.data.type, String))
|
||||||
self.assertEquals(self.table.c.data.type.length, 21)
|
if self.engine.name == 'firebird':
|
||||||
|
self.assertEquals(self.table.c.data.type.length, 43 * 4)
|
||||||
|
else:
|
||||||
|
self.assertEquals(self.table.c.data.type.length, 43)
|
||||||
|
|
||||||
# Different type
|
# Different type
|
||||||
self.assert_(isinstance(self.table.c.id.type, Integer))
|
self.assert_(isinstance(self.table.c.id.type, Integer))
|
||||||
self.assertEquals(self.table.c.id.nullable, False)
|
self.assertEquals(self.table.c.id.nullable, False)
|
||||||
|
|
||||||
|
if not self.engine.name == 'firebird':
|
||||||
self.table.c.id.alter(type=String(20))
|
self.table.c.id.alter(type=String(20))
|
||||||
self.assertEquals(self.table.c.id.nullable, False)
|
self.assertEquals(self.table.c.id.nullable, False)
|
||||||
self.refresh_table(self.table.name)
|
self.refresh_table(self.table.name)
|
||||||
@ -511,7 +525,7 @@ class TestColumnChange(fixture.DB):
|
|||||||
self.assert_(row['data'] is None, row['data'])
|
self.assert_(row['data'] is None, row['data'])
|
||||||
|
|
||||||
|
|
||||||
@fixture.usedb()
|
@fixture.usedb(not_supported='firebird')
|
||||||
def test_null(self):
|
def test_null(self):
|
||||||
"""Can change a column's null constraint"""
|
"""Can change a column's null constraint"""
|
||||||
self.assertEquals(self.table.c.data.nullable, True)
|
self.assertEquals(self.table.c.data.nullable, True)
|
||||||
|
@ -6,6 +6,7 @@ from sqlalchemy.util import *
|
|||||||
from sqlalchemy.exc import *
|
from sqlalchemy.exc import *
|
||||||
|
|
||||||
from migrate.changeset import *
|
from migrate.changeset import *
|
||||||
|
from migrate.changeset.exceptions import *
|
||||||
|
|
||||||
from test import fixture
|
from test import fixture
|
||||||
|
|
||||||
@ -31,8 +32,8 @@ class CommonTestConstraint(fixture.DB):
|
|||||||
self.meta = MetaData(self.engine)
|
self.meta = MetaData(self.engine)
|
||||||
self.tablename = 'mytable'
|
self.tablename = 'mytable'
|
||||||
self.table = Table(self.tablename, self.meta,
|
self.table = Table(self.tablename, self.meta,
|
||||||
Column('id', Integer),
|
Column('id', Integer, nullable=False),
|
||||||
Column('fkey', Integer),
|
Column('fkey', Integer, nullable=False),
|
||||||
mysql_engine='InnoDB')
|
mysql_engine='InnoDB')
|
||||||
if self.engine.has_table(self.table.name):
|
if self.engine.has_table(self.table.name):
|
||||||
self.table.drop()
|
self.table.drop()
|
||||||
@ -49,20 +50,21 @@ class TestConstraint(CommonTestConstraint):
|
|||||||
|
|
||||||
def _define_pk(self, *cols):
|
def _define_pk(self, *cols):
|
||||||
# Add a pk by creating a PK constraint
|
# Add a pk by creating a PK constraint
|
||||||
|
if (self.engine.name in ('oracle', 'firebird')):
|
||||||
|
# Can't drop Oracle PKs without an explicit name
|
||||||
|
pk = PrimaryKeyConstraint(table=self.table, name='temp_pk_key', *cols)
|
||||||
|
else:
|
||||||
pk = PrimaryKeyConstraint(table=self.table, *cols)
|
pk = PrimaryKeyConstraint(table=self.table, *cols)
|
||||||
self.assertEquals(list(pk.columns), list(cols))
|
self.assertEquals(list(pk.columns), list(cols))
|
||||||
if self.url.startswith('oracle'):
|
|
||||||
# Can't drop Oracle PKs without an explicit name
|
|
||||||
pk.name = 'fgsfds'
|
|
||||||
pk.create()
|
pk.create()
|
||||||
self.refresh_table()
|
self.refresh_table()
|
||||||
if not self.url.startswith('sqlite'):
|
if not self.url.startswith('sqlite'):
|
||||||
self.assertEquals(list(self.table.primary_key), list(cols))
|
self.assertEquals(list(self.table.primary_key), list(cols))
|
||||||
|
|
||||||
# Drop the PK constraint
|
# Drop the PK constraint
|
||||||
if not self.url.startswith('oracle'):
|
#if (self.engine.name in ('oracle', 'firebird')):
|
||||||
# Apparently Oracle PK names aren't introspected
|
# # Apparently Oracle PK names aren't introspected
|
||||||
pk.name = self.table.primary_key.name
|
# pk.name = self.table.primary_key.name
|
||||||
pk.drop()
|
pk.drop()
|
||||||
self.refresh_table()
|
self.refresh_table()
|
||||||
self.assertEquals(len(self.table.primary_key), 0)
|
self.assertEquals(len(self.table.primary_key), 0)
|
||||||
@ -113,18 +115,23 @@ class TestConstraint(CommonTestConstraint):
|
|||||||
@fixture.usedb()
|
@fixture.usedb()
|
||||||
def test_define_pk_multi(self):
|
def test_define_pk_multi(self):
|
||||||
"""Multicolumn PK constraints can be defined, created, and dropped"""
|
"""Multicolumn PK constraints can be defined, created, and dropped"""
|
||||||
#self.engine.echo=True
|
|
||||||
self._define_pk(self.table.c.id, self.table.c.fkey)
|
self._define_pk(self.table.c.id, self.table.c.fkey)
|
||||||
|
|
||||||
@fixture.usedb()
|
@fixture.usedb()
|
||||||
def test_drop_cascade(self):
|
def test_drop_cascade(self):
|
||||||
"""Drop constraint cascaded"""
|
"""Drop constraint cascaded"""
|
||||||
|
|
||||||
pk = PrimaryKeyConstraint('fkey', table=self.table, name="id_pkey")
|
pk = PrimaryKeyConstraint('fkey', table=self.table, name="id_pkey")
|
||||||
pk.create()
|
pk.create()
|
||||||
self.refresh_table()
|
self.refresh_table()
|
||||||
|
|
||||||
# Drop the PK constraint forcing cascade
|
# Drop the PK constraint forcing cascade
|
||||||
|
try:
|
||||||
pk.drop(cascade=True)
|
pk.drop(cascade=True)
|
||||||
|
except NotSupportedError:
|
||||||
|
if self.engine.name == 'firebird':
|
||||||
|
pass
|
||||||
|
|
||||||
# TODO: add real assertion if it was added
|
# TODO: add real assertion if it was added
|
||||||
|
|
||||||
@fixture.usedb(supported=['mysql'])
|
@fixture.usedb(supported=['mysql'])
|
||||||
@ -149,10 +156,10 @@ class TestConstraint(CommonTestConstraint):
|
|||||||
cons.create()
|
cons.create()
|
||||||
self.refresh_table()
|
self.refresh_table()
|
||||||
|
|
||||||
self.table.insert(values={'id': 4}).execute()
|
self.table.insert(values={'id': 4, 'fkey': 1}).execute()
|
||||||
try:
|
try:
|
||||||
self.table.insert(values={'id': 1}).execute()
|
self.table.insert(values={'id': 1, 'fkey': 1}).execute()
|
||||||
except IntegrityError:
|
except (IntegrityError, ProgrammingError):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self.fail()
|
self.fail()
|
||||||
@ -160,8 +167,8 @@ class TestConstraint(CommonTestConstraint):
|
|||||||
# Remove the name, drop the constraint; it should succeed
|
# Remove the name, drop the constraint; it should succeed
|
||||||
cons.drop()
|
cons.drop()
|
||||||
self.refresh_table()
|
self.refresh_table()
|
||||||
self.table.insert(values={'id': 2}).execute()
|
self.table.insert(values={'id': 2, 'fkey': 2}).execute()
|
||||||
self.table.insert(values={'id': 1}).execute()
|
self.table.insert(values={'id': 1, 'fkey': 2}).execute()
|
||||||
|
|
||||||
|
|
||||||
class TestAutoname(CommonTestConstraint):
|
class TestAutoname(CommonTestConstraint):
|
||||||
@ -170,7 +177,7 @@ class TestAutoname(CommonTestConstraint):
|
|||||||
"""
|
"""
|
||||||
level = fixture.DB.CONNECT
|
level = fixture.DB.CONNECT
|
||||||
|
|
||||||
@fixture.usedb(not_supported='oracle')
|
@fixture.usedb(not_supported=['oracle', 'firebird'])
|
||||||
def test_autoname_pk(self):
|
def test_autoname_pk(self):
|
||||||
"""PrimaryKeyConstraints can guess their name if None is given"""
|
"""PrimaryKeyConstraints can guess their name if None is given"""
|
||||||
# Don't supply a name; it should create one
|
# Don't supply a name; it should create one
|
||||||
@ -197,7 +204,7 @@ class TestAutoname(CommonTestConstraint):
|
|||||||
cons.name = None
|
cons.name = None
|
||||||
cons.drop()
|
cons.drop()
|
||||||
|
|
||||||
@fixture.usedb(not_supported=['oracle', 'sqlite'])
|
@fixture.usedb(not_supported=['oracle', 'sqlite', 'firebird'])
|
||||||
def test_autoname_fk(self):
|
def test_autoname_fk(self):
|
||||||
"""ForeignKeyConstraints can guess their name if None is given"""
|
"""ForeignKeyConstraints can guess their name if None is given"""
|
||||||
cons = PrimaryKeyConstraint(self.table.c.id)
|
cons = PrimaryKeyConstraint(self.table.c.id)
|
||||||
@ -231,12 +238,11 @@ class TestAutoname(CommonTestConstraint):
|
|||||||
cons.create()
|
cons.create()
|
||||||
self.refresh_table()
|
self.refresh_table()
|
||||||
|
|
||||||
|
|
||||||
if not self.engine.name == 'mysql':
|
if not self.engine.name == 'mysql':
|
||||||
self.table.insert(values={'id': 4}).execute()
|
self.table.insert(values={'id': 4, 'fkey': 1}).execute()
|
||||||
try:
|
try:
|
||||||
self.table.insert(values={'id': 1}).execute()
|
self.table.insert(values={'id': 1, 'fkey': 2}).execute()
|
||||||
except IntegrityError:
|
except (IntegrityError, ProgrammingError):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self.fail()
|
self.fail()
|
||||||
@ -245,8 +251,8 @@ class TestAutoname(CommonTestConstraint):
|
|||||||
cons.name = None
|
cons.name = None
|
||||||
cons.drop()
|
cons.drop()
|
||||||
self.refresh_table()
|
self.refresh_table()
|
||||||
self.table.insert(values={'id': 2}).execute()
|
self.table.insert(values={'id': 2, 'fkey': 2}).execute()
|
||||||
self.table.insert(values={'id': 1}).execute()
|
self.table.insert(values={'id': 1, 'fkey': 3}).execute()
|
||||||
|
|
||||||
@fixture.usedb(not_supported=['oracle', 'sqlite'])
|
@fixture.usedb(not_supported=['oracle', 'sqlite'])
|
||||||
def test_autoname_unique(self):
|
def test_autoname_unique(self):
|
||||||
@ -255,11 +261,11 @@ class TestAutoname(CommonTestConstraint):
|
|||||||
cons.create()
|
cons.create()
|
||||||
self.refresh_table()
|
self.refresh_table()
|
||||||
|
|
||||||
|
self.table.insert(values={'fkey': 4, 'id': 1}).execute()
|
||||||
self.table.insert(values={'fkey': 4}).execute()
|
|
||||||
try:
|
try:
|
||||||
self.table.insert(values={'fkey': 4}).execute()
|
self.table.insert(values={'fkey': 4, 'id': 2}).execute()
|
||||||
except IntegrityError:
|
except (sqlalchemy.exc.IntegrityError,
|
||||||
|
sqlalchemy.exc.ProgrammingError):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self.fail()
|
self.fail()
|
||||||
@ -268,5 +274,5 @@ class TestAutoname(CommonTestConstraint):
|
|||||||
cons.name = None
|
cons.name = None
|
||||||
cons.drop()
|
cons.drop()
|
||||||
self.refresh_table()
|
self.refresh_table()
|
||||||
self.table.insert(values={'fkey': 4}).execute()
|
self.table.insert(values={'fkey': 4, 'id': 2}).execute()
|
||||||
self.table.insert(values={'fkey': 4}).execute()
|
self.table.insert(values={'fkey': 4, 'id': 1}).execute()
|
||||||
|
@ -139,6 +139,7 @@ class TestSchemaDiff(fixture.DB):
|
|||||||
# Not even using sqlalchemy.PassiveDefault helps because we're doing explicit column select.
|
# Not even using sqlalchemy.PassiveDefault helps because we're doing explicit column select.
|
||||||
self.engine.execute(self.table.delete(), id=dataId)
|
self.engine.execute(self.table.delete(), id=dataId)
|
||||||
|
|
||||||
|
if not self.engine.name == 'firebird':
|
||||||
# Change column nullable in model.
|
# Change column nullable in model.
|
||||||
self.meta.remove(self.table)
|
self.meta.remove(self.table)
|
||||||
self.table = Table(self.table_name,self.meta,
|
self.table = Table(self.table_name,self.meta,
|
||||||
@ -155,4 +156,3 @@ class TestSchemaDiff(fixture.DB):
|
|||||||
# Remove table from model.
|
# Remove table from model.
|
||||||
self.meta.remove(self.table)
|
self.meta.remove(self.table)
|
||||||
assertDiff(True, [], [self.table_name], [])
|
assertDiff(True, [], [self.table_name], [])
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user