finally, tests pass for all supported dialects

This commit is contained in:
iElectric 2009-06-21 12:51:33 +00:00
parent 4a2cd3797f
commit 17cc5f36e6
7 changed files with 67 additions and 60 deletions

View File

@ -47,35 +47,36 @@ Dialect support
| Operation / Dialect | :ref:`sqlite <sqlite-d>` | :ref:`postgres <postgres-d>` | :ref:`mysql <mysql-d>` | :ref:`oracle <oracle-d>` | firebird | mssql |
| | | | | | | |
+=========================================================+==========================+==============================+========================+===========================+==========+=======+
| :ref:`ALTER TABLE RENAME TABLE <table-rename>` | yes | yes | yes | | | |
| :ref:`ALTER TABLE RENAME TABLE <table-rename>` | yes | yes | yes | yes | | |
| | | | | | | |
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
| :ref:`ALTER TABLE RENAME COLUMN <column-alter>` | yes | yes | yes | | | |
| :ref:`ALTER TABLE RENAME COLUMN <column-alter>` | yes | yes | yes | yes | | |
| | (workaround) [#1]_ | | | | | |
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
| :ref:`ALTER TABLE ADD COLUMN <column-create>` | yes | yes | yes | | | |
| :ref:`ALTER TABLE ADD COLUMN <column-create>` | yes | yes | yes | yes | | |
| | (with limitations) [#2]_ | | | | | |
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
| :ref:`ALTER TABLE DROP COLUMN <column-drop>` | yes | yes | yes | | | |
| :ref:`ALTER TABLE DROP COLUMN <column-drop>` | yes | yes | yes | yes | | |
| | (workaround) [#1]_ | | | | | |
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
| :ref:`ALTER TABLE ALTER COLUMN <column-alter>` | no | yes | yes | | | |
| :ref:`ALTER TABLE ALTER COLUMN <column-alter>` | no | yes | yes | yes | | |
| | | | | (with limitations) [#3]_ | | |
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
| :ref:`ALTER TABLE ADD CONSTRAINT <constraint-tutorial>` | no | yes | yes | yes | | |
| | | | | | | |
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
| :ref:`ALTER TABLE ADD CONSTRAINT <constraint-tutorial>` | no | yes | yes | | | |
| :ref:`ALTER TABLE DROP CONSTRAINT <constraint-tutorial>`| no | yes | yes | yes | | |
| | | | | | | |
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
| :ref:`ALTER TABLE DROP CONSTRAINT <constraint-tutorial>`| no | yes | yes | | | |
| | | | | | | |
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
| :ref:`RENAME INDEX <index-rename>` | no | yes | no | | | |
| :ref:`RENAME INDEX <index-rename>` | no | yes | no | yes | | |
| | | | | | | |
+---------------------------------------------------------+--------------------------+------------------------------+------------------------+---------------------------+----------+-------+
.. [#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.
.. [#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.
Documentation
-------------

View File

@ -220,10 +220,7 @@ class ANSISchemaChanger(AlterTableVisitor, SchemaGenerator):
def _visit_column_name(self, table, col_name, delta):
new_name = delta['name']
self.start_alter_table(table)
# TODO: use preparer.format_column
self.append('RENAME COLUMN %s TO %s' % \
(self.preparer.quote_identifier(col_name),
self.preparer.quote_identifier(new_name)))
self.append('RENAME COLUMN %s TO %s' % (col_name, new_name))
class ANSIConstraintCommon(AlterTableVisitor):

View File

@ -32,19 +32,25 @@ class OracleSchemaChanger(OracleSchemaGenerator, ansisql.ANSISchemaChanger):
column.nullable = orig
return ret
def visit_column(self, delta):
def visit_column(self, column):
delta = column.delta
keys = delta.keys()
if 'type' in keys or 'nullable' in keys or 'default' in keys \
or 'server_default' in keys:
self._run_subvisit(delta, self._visit_column_change)
if len(set(('type', 'nullable', 'server_default')).intersection(keys)):
self._run_subvisit(delta,
self._visit_column_change,
start_alter=False)
# change name as the last action to avoid conflicts
if 'name' in keys:
self._run_subvisit(delta, self._visit_column_name)
self._run_subvisit(delta,
self._visit_column_name,
start_alter=False)
def _visit_column_change(self, table, col_name, delta):
if not hasattr(delta, 'result_column'):
# Oracle needs the whole column definition, not just a lone name/type
raise exceptions.NotSupportedError(
"A column object is required to do this")
"A column object must be present in table to alter it")
column = delta.result_column
# Oracle cannot drop a default once created, but it can set it
@ -74,9 +80,10 @@ class OracleSchemaChanger(OracleSchemaGenerator, ansisql.ANSISchemaChanger):
if dropdefault_hack:
column.server_default = None
self.start_alter_table(self.preparer.format_table(table))
self.append("MODIFY ")
self.start_alter_table(table)
self.append("MODIFY (")
self.append(colspec)
self.append(")")
class OracleConstraintCommon(object):

View File

@ -35,7 +35,9 @@ class ControlledSchema(object):
if not hasattr(self, 'table') or self.table is None:
try:
self.table = Table(tname, self.meta, autoload=True)
except sa_exceptions.NoSuchTableError:
except (sa_exceptions.NoSuchTableError,
AssertionError):
# assertionerror is raised if no table is found in oracle db
raise exceptions.DatabaseNotControlledError(tname)
# TODO?: verify that the table is correct (# cols, etc.)

View File

@ -9,9 +9,6 @@ from migrate.changeset.schema import _ColumnDelta
from test import fixture
# TODO: test quoting
# TODO: test all other constraints on create column, test defaults
class TestAddDropColumn(fixture.DB):
"""Test add/drop column through all possible interfaces
also test for constraints"""
@ -265,7 +262,7 @@ class TestAddDropColumn(fixture.DB):
col = Column('data', String(244), server_default='foobar')
col.create(self.table)
self.table.insert().execute()
self.table.insert(values={'id': 10}).execute()
row = self.table.select(autocommit=True).execute().fetchone()
self.assertEqual(u'foobar', row['data'])
@ -273,6 +270,8 @@ class TestAddDropColumn(fixture.DB):
# TODO: test sequence
# 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 quoting
class TestRename(fixture.DB):
@ -415,8 +414,6 @@ class TestColumnChange(fixture.DB):
self.refresh_table(self.table.name)
self.assert_('data' not in self.table.c.keys())
self.assert_('atad' in self.table.c.keys())
#self.assertRaises(AttributeError,getattr,self.table.c,'data')
self.table.c.atad # Should not raise exception
self.assertEquals(num_rows(self.table.c.atad, content), 1)
# ...as a method, given a new name
@ -482,13 +479,12 @@ class TestColumnChange(fixture.DB):
self.refresh_table(self.table.name)
self.assert_(isinstance(self.table.c.id.type, String))
@fixture.usedb(not_supported='mysql')
@fixture.usedb()
def test_default(self):
"""Can change a column's server_default value (DefaultClauses only)
Only DefaultClauses are changed here: others are managed by the
application / by SA
"""
#self.engine.echo=True
self.assertEquals(self.table.c.data.server_default.arg, 'tluafed')
# Just the new default

View File

@ -31,7 +31,7 @@ class CommonTestConstraint(fixture.DB):
self.meta = MetaData(self.engine)
self.tablename = 'mytable'
self.table = Table(self.tablename, self.meta,
Column('id', Integer, unique=True),
Column('id', Integer),
Column('fkey', Integer),
mysql_engine='InnoDB')
if self.engine.has_table(self.table.name):
@ -81,7 +81,6 @@ class TestConstraint(CommonTestConstraint):
fk = ForeignKeyConstraint([self.table.c.fkey],
[self.table.c.id],
name="fk_id_fkey",
onupdate="CASCADE",
ondelete="CASCADE")
self.assert_(self.table.c.fkey.foreign_keys._list is not [])
self.assertEquals(list(fk.columns), [self.table.c.fkey])
@ -96,7 +95,6 @@ class TestConstraint(CommonTestConstraint):
# test for ondelete/onupdate
fkey = self.table.c.fkey.foreign_keys._list[0]
self.assertEquals(fkey.onupdate, "CASCADE")
self.assertEquals(fkey.ondelete, "CASCADE")
# TODO: test on real db if it was set
@ -110,7 +108,7 @@ class TestConstraint(CommonTestConstraint):
@fixture.usedb()
def test_define_pk(self):
"""PK constraints can be defined, created, and dropped"""
self._define_pk(self.table.c.id)
self._define_pk(self.table.c.fkey)
@fixture.usedb()
def test_define_pk_multi(self):
@ -121,7 +119,7 @@ class TestConstraint(CommonTestConstraint):
@fixture.usedb()
def test_drop_cascade(self):
"""Drop constraint cascaded"""
pk = PrimaryKeyConstraint('id', table=self.table, name="id_pkey")
pk = PrimaryKeyConstraint('fkey', table=self.table, name="id_pkey")
pk.create()
self.refresh_table()
@ -202,6 +200,9 @@ class TestAutoname(CommonTestConstraint):
@fixture.usedb(not_supported=['oracle', 'sqlite'])
def test_autoname_fk(self):
"""ForeignKeyConstraints can guess their name if None is given"""
cons = PrimaryKeyConstraint(self.table.c.id)
cons.create()
cons = ForeignKeyConstraint([self.table.c.fkey], [self.table.c.id])
cons.create()
self.refresh_table()

View File

@ -85,9 +85,10 @@ class TestSchemaDiff(fixture.DB):
)
''')
# Add data, later we'll make sure it's still present.
result = self.engine.execute(self.table.insert(), id=1, name=u'mydata')
dataId = result.last_inserted_ids()[0]
if not self.engine.name == 'oracle':
# Add data, later we'll make sure it's still present.
result = self.engine.execute(self.table.insert(), id=1, name=u'mydata')
dataId = result.last_inserted_ids()[0]
# Modify table in model (by removing it and adding it back to model) -- drop column data and add column data2.
self.meta.remove(self.table)
@ -102,22 +103,23 @@ class TestSchemaDiff(fixture.DB):
self._applyLatestModel()
assertDiff(False, [], [], [])
# Make sure data is still present.
result = self.engine.execute(self.table.select(self.table.c.id==dataId))
rows = result.fetchall()
eq_(len(rows), 1)
eq_(rows[0].name, 'mydata')
if not self.engine.name == 'oracle':
# Make sure data is still present.
result = self.engine.execute(self.table.select(self.table.c.id==dataId))
rows = result.fetchall()
eq_(len(rows), 1)
eq_(rows[0].name, 'mydata')
# Add data, later we'll make sure it's still present.
result = self.engine.execute(self.table.insert(), id=2, name=u'mydata2', data2=123)
dataId2 = result.last_inserted_ids()[0]
# Add data, later we'll make sure it's still present.
result = self.engine.execute(self.table.insert(), id=2, name=u'mydata2', data2=123)
dataId2 = result.last_inserted_ids()[0]
# Change column type in model.
self.meta.remove(self.table)
self.table = Table(self.table_name,self.meta,
Column('id',Integer(),primary_key=True),
Column('name',UnicodeText(length=None)),
Column('data2',UnicodeText(),nullable=True),
Column('data2',String(255),nullable=True),
)
assertDiff(True, [], [], [self.table_name]) # TODO test type diff
@ -125,23 +127,24 @@ class TestSchemaDiff(fixture.DB):
self._applyLatestModel()
assertDiff(False, [], [], [])
# Make sure data is still present.
result = self.engine.execute(self.table.select(self.table.c.id==dataId2))
rows = result.fetchall()
self.assertEquals(len(rows), 1)
self.assertEquals(rows[0].name, 'mydata2')
self.assertEquals(rows[0].data2, '123')
# Delete data, since we're about to make a required column.
# Not even using sqlalchemy.PassiveDefault helps because we're doing explicit column select.
self.engine.execute(self.table.delete(), id=dataId)
if not self.engine.name == 'oracle':
# Make sure data is still present.
result = self.engine.execute(self.table.select(self.table.c.id==dataId2))
rows = result.fetchall()
self.assertEquals(len(rows), 1)
self.assertEquals(rows[0].name, 'mydata2')
self.assertEquals(rows[0].data2, '123')
# Delete data, since we're about to make a required column.
# Not even using sqlalchemy.PassiveDefault helps because we're doing explicit column select.
self.engine.execute(self.table.delete(), id=dataId)
# Change column nullable in model.
self.meta.remove(self.table)
self.table = Table(self.table_name,self.meta,
Column('id',Integer(),primary_key=True),
Column('name',UnicodeText(length=None)),
Column('data2',UnicodeText(),nullable=False),
Column('data2',String(255),nullable=False),
)
assertDiff(True, [], [], [self.table_name]) # TODO test nullable diff