adding basic support for firebird, fixes #55

This commit is contained in:
iElectric 2009-06-22 10:22:06 +00:00
parent 17cc5f36e6
commit a8c31eb25f
12 changed files with 198 additions and 96 deletions

View File

@ -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>`

View File

@ -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

View File

@ -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
------------- -------------

View File

@ -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

View File

@ -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):

View 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

View File

@ -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,
} }

View File

@ -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()

View File

@ -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), )

View File

@ -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)

View File

@ -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()

View File

@ -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], [])