make migrate.changeset.databases PEP-8 clean and add it to the API docs

This commit is contained in:
jan.dittberner 2009-01-25 12:52:33 +00:00
parent 728b677e56
commit eb00570991
7 changed files with 233 additions and 93 deletions

View File

@ -9,13 +9,54 @@ Module :mod:`migrate.changeset`
:members: :members:
:synopsis: Database changeset management :synopsis: Database changeset management
Module :mod:`migrate.changeset.ansisql` Module :mod:`ansisql <migrate.changeset.ansisql>`
--------------------------------------- -------------------------------------------------
.. automodule:: migrate.changeset.ansisql .. automodule:: migrate.changeset.ansisql
:members: :members:
:synopsis: Standard SQL implementation for altering database schemas :synopsis: Standard SQL implementation for altering database schemas
Module :mod:`databases <migrate.changeset.databases>`
-----------------------------------------------------
.. automodule:: migrate.changeset.databases
:members:
:synopsis: Database specific changeset implementations
Module :mod:`mysql <migrate.changeset.databases.mysql>`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: migrate.changeset.databases.mysql
:members:
:synopsis: MySQL database specific changeset implementations
Module :mod:`oracle <migrate.changeset.databases.oracle>`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: migrate.changeset.databases.oracle
:members:
:synopsis: Oracle database specific changeset implementations
Module :mod:`postgres <migrate.changeset.databases.postgres>`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: migrate.changeset.databases.postgres
:members:
:synopsis: PostgreSQL database specific changeset implementations
Module :mod:`sqlite <migrate.changeset.databases.slite>`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: migrate.changeset.databases.sqlite
:members:
:synopsis: SQLite database specific changeset implementations
Module :mod:`visitor <migrate.changeset.databases.visitor>`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: migrate.changeset.databases.visitor
:members:
Module :mod:`migrate.versioning` Module :mod:`migrate.versioning`
================================ ================================

View File

@ -1,3 +1,7 @@
"""
This module contains database dialect specific changeset
implementations.
"""
__all__=[ __all__=[
'postgres', 'postgres',
'sqlite', 'sqlite',

View File

@ -1,52 +1,74 @@
from migrate.changeset import ansisql,exceptions """
MySQL database specific implementations of changeset classes.
"""
from migrate.changeset import ansisql, exceptions
from sqlalchemy.databases import mysql as sa_base from sqlalchemy.databases import mysql as sa_base
#import sqlalchemy as sa #import sqlalchemy as sa
MySQLSchemaGenerator = sa_base.MySQLSchemaGenerator MySQLSchemaGenerator = sa_base.MySQLSchemaGenerator
class MySQLColumnGenerator(MySQLSchemaGenerator,ansisql.ANSIColumnGenerator):
class MySQLColumnGenerator(MySQLSchemaGenerator, ansisql.ANSIColumnGenerator):
def _do_quote_table_identifier(self, identifier): def _do_quote_table_identifier(self, identifier):
return '%s'%identifier return '%s'%identifier
pass pass
class MySQLColumnDropper(ansisql.ANSIColumnDropper): class MySQLColumnDropper(ansisql.ANSIColumnDropper):
def _do_quote_table_identifier(self, identifier): def _do_quote_table_identifier(self, identifier):
return '%s'%identifier return '%s'%identifier
def _do_quote_column_identifier(self, identifier): def _do_quote_column_identifier(self, identifier):
return '%s'%identifier return '%s'%identifier
class MySQLSchemaChanger(MySQLSchemaGenerator,ansisql.ANSISchemaChanger):
def visit_column(self,delta): class MySQLSchemaChanger(MySQLSchemaGenerator, ansisql.ANSISchemaChanger):
def visit_column(self, delta):
keys = delta.keys() keys = delta.keys()
if 'type' in keys or 'nullable' in keys or 'name' in keys: if 'type' in keys or 'nullable' in keys or 'name' in keys:
self._run_subvisit(delta,self._visit_column_change) self._run_subvisit(delta, self._visit_column_change)
if 'server_default' in keys: if 'server_default' in keys:
# Column name might have changed above # Column name might have changed above
col_name = delta.get('name',delta.current_name) col_name = delta.get('name', delta.current_name)
self._run_subvisit(delta,self._visit_column_default,col_name=col_name) self._run_subvisit(delta, self._visit_column_default,
def _visit_column_change(self,table_name,col_name,delta): col_name=col_name)
if not hasattr(delta,'result_column'):
# Mysql needs the whole column definition, not just a lone name/type def _visit_column_change(self, table_name, col_name, delta):
if not hasattr(delta, 'result_column'):
# Mysql needs the whole column definition, not just a lone
# name/type
raise exceptions.NotSupportedError( raise exceptions.NotSupportedError(
"A column object is required to do this") "A column object is required to do this")
column = delta.result_column column = delta.result_column
if not column.table: column.table = delta.table # needed by get_column_specification # needed by get_column_specification
if not column.table:
column.table = delta.table
colspec = self.get_column_specification(column) colspec = self.get_column_specification(column)
self.start_alter_table(table_name) self.start_alter_table(table_name)
self.append("CHANGE COLUMN ") self.append("CHANGE COLUMN ")
self.append(col_name) self.append(col_name)
self.append(' ') self.append(' ')
self.append(colspec) self.append(colspec)
def visit_index(self,param):
def visit_index(self, param):
# If MySQL can do this, I can't find how # If MySQL can do this, I can't find how
raise exceptions.NotSupportedError("MySQL cannot rename indexes") raise exceptions.NotSupportedError("MySQL cannot rename indexes")
def _do_quote_table_identifier(self, identifier): def _do_quote_table_identifier(self, identifier):
return '%s'%identifier return '%s'%identifier
class MySQLConstraintGenerator(ansisql.ANSIConstraintGenerator): class MySQLConstraintGenerator(ansisql.ANSIConstraintGenerator):
def _do_quote_table_identifier(self, identifier): def _do_quote_table_identifier(self, identifier):
return '%s'%identifier return '%s'%identifier
class MySQLConstraintDropper(ansisql.ANSIConstraintDropper): class MySQLConstraintDropper(ansisql.ANSIConstraintDropper):
#def visit_constraint(self,constraint): #def visit_constraint(self,constraint):
# if isinstance(constraint,sqlalchemy.schema.PrimaryKeyConstraint): # if isinstance(constraint,sqlalchemy.schema.PrimaryKeyConstraint):
@ -54,12 +76,13 @@ class MySQLConstraintDropper(ansisql.ANSIConstraintDropper):
# elif isinstance(constraint,sqlalchemy.schema.ForeignKeyConstraint): # elif isinstance(constraint,sqlalchemy.schema.ForeignKeyConstraint):
# return self._visit_constraint_fk(constraint) # return self._visit_constraint_fk(constraint)
# return super(MySQLConstraintDropper,self).visit_constraint(constraint) # return super(MySQLConstraintDropper,self).visit_constraint(constraint)
def visit_migrate_primary_key_constraint(self,constraint):
def visit_migrate_primary_key_constraint(self, constraint):
self.start_alter_table(constraint) self.start_alter_table(constraint)
self.append("DROP PRIMARY KEY") self.append("DROP PRIMARY KEY")
self.execute() self.execute()
def visit_migrate_foreign_key_constraint(self,constraint): def visit_migrate_foreign_key_constraint(self, constraint):
self.start_alter_table(constraint) self.start_alter_table(constraint)
self.append("DROP FOREIGN KEY ") self.append("DROP FOREIGN KEY ")
self.append(constraint.name) self.append(constraint.name)
@ -68,6 +91,7 @@ class MySQLConstraintDropper(ansisql.ANSIConstraintDropper):
def _do_quote_table_identifier(self, identifier): def _do_quote_table_identifier(self, identifier):
return '%s'%identifier return '%s'%identifier
class MySQLDialect(ansisql.ANSIDialect): class MySQLDialect(ansisql.ANSIDialect):
columngenerator = MySQLColumnGenerator columngenerator = MySQLColumnGenerator
columndropper = MySQLColumnDropper columndropper = MySQLColumnDropper

View File

@ -1,54 +1,74 @@
from migrate.changeset import ansisql,exceptions """
Oracle database specific implementations of changeset classes.
"""
from migrate.changeset import ansisql, exceptions
from sqlalchemy.databases import oracle as sa_base from sqlalchemy.databases import oracle as sa_base
import sqlalchemy as sa import sqlalchemy as sa
OracleSchemaGenerator = sa_base.OracleSchemaGenerator OracleSchemaGenerator = sa_base.OracleSchemaGenerator
class OracleColumnGenerator(OracleSchemaGenerator,ansisql.ANSIColumnGenerator):
class OracleColumnGenerator(OracleSchemaGenerator,
ansisql.ANSIColumnGenerator):
pass pass
class OracleColumnDropper(ansisql.ANSIColumnDropper): class OracleColumnDropper(ansisql.ANSIColumnDropper):
pass pass
class OracleSchemaChanger(OracleSchemaGenerator,ansisql.ANSISchemaChanger):
def get_column_specification(self,column,**kwargs):
class OracleSchemaChanger(OracleSchemaGenerator, ansisql.ANSISchemaChanger):
def get_column_specification(self, column, **kwargs):
# Ignore the NOT NULL generated # Ignore the NOT NULL generated
override_nullable = kwargs.pop('override_nullable',None) override_nullable = kwargs.pop('override_nullable', None)
if override_nullable: if override_nullable:
orig = column.nullable orig = column.nullable
column.nullable = True column.nullable = True
ret=super(OracleSchemaChanger,self).get_column_specification(column,**kwargs) ret = super(OracleSchemaChanger, self).get_column_specification(
column, **kwargs)
if override_nullable: if override_nullable:
column.nullable = orig column.nullable = orig
return ret return ret
def visit_column(self,delta): def visit_column(self, delta):
keys = delta.keys() keys = delta.keys()
if 'type' in keys or 'nullable' in keys or 'default' in keys or 'server_default' in keys: if 'type' in keys or 'nullable' in keys or 'default' in keys \
self._run_subvisit(delta,self._visit_column_change) or 'server_default' in keys:
self._run_subvisit(delta, self._visit_column_change)
if 'name' in keys: if 'name' in keys:
self._run_subvisit(delta,self._visit_column_name) self._run_subvisit(delta, self._visit_column_name)
def _visit_column_change(self,table_name,col_name,delta):
if not hasattr(delta,'result_column'): def _visit_column_change(self, table_name, col_name, delta):
# Oracle needs the whole column definition, not just a lone name/type if not hasattr(delta, 'result_column'):
# Oracle needs the whole column definition, not just a
# lone name/type
raise exceptions.NotSupportedError( raise exceptions.NotSupportedError(
"A column object is required to do this") "A column object is required to do this")
column = delta.result_column column = delta.result_column
# Oracle cannot drop a default once created, but it can set it to null. # Oracle cannot drop a default once created, but it can set it
# We'll do that if default=None # to null. We'll do that if default=None
# http://forums.oracle.com/forums/message.jspa?messageID=1273234#1273234 # http://forums.oracle.com/forums/message.jspa?\
dropdefault_hack = (column.server_default is None and 'server_default' in delta.keys()) # messageID=1273234#1273234
# Oracle apparently doesn't like it when we say "not null" if the dropdefault_hack = (column.server_default is None \
# column's already not null. Fudge it, so we don't need a new function and 'server_default' in delta.keys())
notnull_hack = ((not column.nullable) and ('nullable' not in delta.keys())) # Oracle apparently doesn't like it when we say "not null" if
# We need to specify NULL if we're removing a NOT NULL constraint # the column's already not null. Fudge it, so we don't need a
# new function
notnull_hack = ((not column.nullable) \
and ('nullable' not in delta.keys()))
# We need to specify NULL if we're removing a NOT NULL
# constraint
null_hack = (column.nullable and ('nullable' in delta.keys())) null_hack = (column.nullable and ('nullable' in delta.keys()))
if dropdefault_hack: if dropdefault_hack:
column.server_default = sa.PassiveDefault(sa.sql.null()) column.server_default = sa.PassiveDefault(sa.sql.null())
if notnull_hack: if notnull_hack:
column.nullable = True column.nullable = True
colspec=self.get_column_specification(column,override_nullable=null_hack) colspec=self.get_column_specification(column,
override_nullable=null_hack)
if null_hack: if null_hack:
colspec += ' NULL' colspec += ' NULL'
if notnull_hack: if notnull_hack:
@ -59,18 +79,28 @@ class OracleSchemaChanger(OracleSchemaGenerator,ansisql.ANSISchemaChanger):
self.start_alter_table(table_name) self.start_alter_table(table_name)
self.append("MODIFY ") self.append("MODIFY ")
self.append(colspec) self.append(colspec)
class OracleConstraintCommon(object): class OracleConstraintCommon(object):
def get_constraint_name(self,cons):
def get_constraint_name(self, cons):
# Oracle constraints can't guess their name like other DBs # Oracle constraints can't guess their name like other DBs
if not cons.name: if not cons.name:
raise exceptions.NotSupportedError( raise exceptions.NotSupportedError(
"Oracle constraint names must be explicitly stated") "Oracle constraint names must be explicitly stated")
return cons.name return cons.name
class OracleConstraintGenerator(OracleConstraintCommon,ansisql.ANSIConstraintGenerator):
class OracleConstraintGenerator(OracleConstraintCommon,
ansisql.ANSIConstraintGenerator):
pass pass
class OracleConstraintDropper(OracleConstraintCommon,ansisql.ANSIConstraintDropper):
class OracleConstraintDropper(OracleConstraintCommon,
ansisql.ANSIConstraintDropper):
pass pass
class OracleDialect(ansisql.ANSIDialect): class OracleDialect(ansisql.ANSIDialect):
columngenerator = OracleColumnGenerator columngenerator = OracleColumnGenerator
columndropper = OracleColumnDropper columndropper = OracleColumnDropper

View File

@ -1,44 +1,53 @@
"""
`PostgreSQL`_ database specific implementations of changeset classes.
.. _`PostgreSQL`: http://www.postgresql.org/
"""
from migrate.changeset import ansisql from migrate.changeset import ansisql
from sqlalchemy.databases import postgres as sa_base from sqlalchemy.databases import postgres as sa_base
#import sqlalchemy as sa #import sqlalchemy as sa
PGSchemaGenerator = sa_base.PGSchemaGenerator PGSchemaGenerator = sa_base.PGSchemaGenerator
class PGSchemaGeneratorMixin(object): class PGSchemaGeneratorMixin(object):
"""Common code used by the PostgreSQL specific classes."""
def _do_quote_table_identifier(self, identifier): def _do_quote_table_identifier(self, identifier):
return identifier return identifier
def _do_quote_column_identifier(self, identifier): def _do_quote_column_identifier(self, identifier):
return '"%s"'%identifier return '"%s"'%identifier
class PGColumnGenerator(PGSchemaGenerator,ansisql.ANSIColumnGenerator, PGSchemaGeneratorMixin):
def _do_quote_table_identifier(self, identifier): class PGColumnGenerator(PGSchemaGenerator, ansisql.ANSIColumnGenerator,
return identifier PGSchemaGeneratorMixin):
def _do_quote_column_identifier(self, identifier): """PostgreSQL column generator implementation."""
return '"%s"'%identifier pass
class PGColumnDropper(ansisql.ANSIColumnDropper, PGSchemaGeneratorMixin): class PGColumnDropper(ansisql.ANSIColumnDropper, PGSchemaGeneratorMixin):
def _do_quote_table_identifier(self, identifier): """PostgreSQL column dropper implementation."""
return identifier pass
def _do_quote_column_identifier(self, identifier):
return '"%s"'%identifier
class PGSchemaChanger(ansisql.ANSISchemaChanger, PGSchemaGeneratorMixin): class PGSchemaChanger(ansisql.ANSISchemaChanger, PGSchemaGeneratorMixin):
def _do_quote_table_identifier(self, identifier): """PostgreSQL schema changer implementation."""
return identifier pass
def _do_quote_column_identifier(self, identifier):
return '"%s"'%identifier
class PGConstraintGenerator(ansisql.ANSIConstraintGenerator, PGSchemaGeneratorMixin):
def _do_quote_table_identifier(self, identifier):
return identifier
def _do_quote_column_identifier(self, identifier):
return '"%s"'%identifier
class PGConstraintDropper(ansisql.ANSIConstraintDropper, PGSchemaGeneratorMixin): class PGConstraintGenerator(ansisql.ANSIConstraintGenerator,
def _do_quote_table_identifier(self, identifier): PGSchemaGeneratorMixin):
return identifier """PostgreSQL constraint generator implementation."""
def _do_quote_column_identifier(self, identifier): pass
return '"%s"'%identifier
class PGConstraintDropper(ansisql.ANSIConstraintDropper,
PGSchemaGeneratorMixin):
"""PostgreSQL constaint dropper implementation."""
pass
class PGDialect(ansisql.ANSIDialect): class PGDialect(ansisql.ANSIDialect):
columngenerator = PGColumnGenerator columngenerator = PGColumnGenerator

View File

@ -1,11 +1,18 @@
from migrate.changeset import ansisql,constraint,exceptions """
`SQLite`_ database specific implementations of changeset classes.
.. _`SQLite`: http://www.sqlite.org/
"""
from migrate.changeset import ansisql, constraint, exceptions
from sqlalchemy.databases import sqlite as sa_base from sqlalchemy.databases import sqlite as sa_base
from sqlalchemy import Table, MetaData from sqlalchemy import Table, MetaData
#import sqlalchemy as sa #import sqlalchemy as sa
SQLiteSchemaGenerator = sa_base.SQLiteSchemaGenerator SQLiteSchemaGenerator = sa_base.SQLiteSchemaGenerator
class SQLiteHelper(object): class SQLiteHelper(object):
def visit_column(self, param): def visit_column(self, param):
try: try:
table = self._to_table(param.table) table = self._to_table(param.table)
@ -24,50 +31,61 @@ class SQLiteHelper(object):
self.append('DROP TABLE migration_tmp') self.append('DROP TABLE migration_tmp')
self.execute() self.execute()
class SQLiteColumnGenerator(SQLiteSchemaGenerator,ansisql.ANSIColumnGenerator):
class SQLiteColumnGenerator(SQLiteSchemaGenerator,
ansisql.ANSIColumnGenerator):
pass pass
class SQLiteColumnDropper(SQLiteHelper, ansisql.ANSIColumnDropper): class SQLiteColumnDropper(SQLiteHelper, ansisql.ANSIColumnDropper):
def _modify_table(self,table, column):
def _modify_table(self, table, column):
del table.columns[column.name] del table.columns[column.name]
columns = ','.join([c.name for c in table.columns]) return 'INSERT INTO %(table_name)s SELECT %s from migration_tmp' % \
return 'INSERT INTO %(table_name)s SELECT ' + columns + ' from migration_tmp' ','.join([c.name for c in table.columns])
class SQLiteSchemaChanger(SQLiteHelper, ansisql.ANSISchemaChanger): class SQLiteSchemaChanger(SQLiteHelper, ansisql.ANSISchemaChanger):
def _not_supported(self,op):
def _not_supported(self, op):
raise exceptions.NotSupportedError("SQLite does not support " raise exceptions.NotSupportedError("SQLite does not support "
"%s; see http://www.sqlite.org/lang_altertable.html"%op) "%s; see http://www.sqlite.org/lang_altertable.html"%op)
def _modify_table(self, table, delta): def _modify_table(self, table, delta):
column = table.columns[delta.current_name] column = table.columns[delta.current_name]
for k,v in delta.items(): for k, v in delta.items():
setattr(column, k, v) setattr(column, k, v)
return 'INSERT INTO %(table_name)s SELECT * from migration_tmp' return 'INSERT INTO %(table_name)s SELECT * from migration_tmp'
def visit_index(self,param): def visit_index(self, param):
self._not_supported('ALTER INDEX') self._not_supported('ALTER INDEX')
def _do_quote_column_identifier(self, identifier): def _do_quote_column_identifier(self, identifier):
return '"%s"'%identifier return '"%s"'%identifier
class SQLiteConstraintGenerator(ansisql.ANSIConstraintGenerator): class SQLiteConstraintGenerator(ansisql.ANSIConstraintGenerator):
def visit_migrate_primary_key_constraint(self,constraint):
def visit_migrate_primary_key_constraint(self, constraint):
tmpl = "CREATE UNIQUE INDEX %s ON %s ( %s )" tmpl = "CREATE UNIQUE INDEX %s ON %s ( %s )"
cols = ','.join([c.name for c in constraint.columns]) cols = ','.join([c.name for c in constraint.columns])
tname = constraint.table.name tname = constraint.table.name
name = constraint.name name = constraint.name
msg = tmpl%(name,tname,cols) msg = tmpl % (name, tname, cols)
self.append(msg) self.append(msg)
self.execute() self.execute()
class SQLiteConstraintDropper(ansisql.ANSIColumnDropper): class SQLiteConstraintDropper(ansisql.ANSIColumnDropper):
def visit_migrate_primary_key_constraint(self,constraint):
def visit_migrate_primary_key_constraint(self, constraint):
tmpl = "DROP INDEX %s " tmpl = "DROP INDEX %s "
name = constraint.name name = constraint.name
msg = tmpl%(name) msg = tmpl % (name)
self.append(msg) self.append(msg)
self.execute() self.execute()
class SQLiteDialect(ansisql.ANSIDialect): class SQLiteDialect(ansisql.ANSIDialect):
columngenerator = SQLiteColumnGenerator columngenerator = SQLiteColumnGenerator
columndropper = SQLiteColumnDropper columndropper = SQLiteColumnDropper

View File

@ -1,20 +1,34 @@
"""
Module for visitor class mapping.
"""
import sqlalchemy as sa import sqlalchemy as sa
from migrate.changeset.databases import sqlite,postgres,mysql,oracle from migrate.changeset.databases import sqlite, postgres, mysql, oracle
from migrate.changeset import ansisql from migrate.changeset import ansisql
# Map SA dialects to the corresponding Migrate extensions # Map SA dialects to the corresponding Migrate extensions
dialects = { dialects = {
sa.engine.default.DefaultDialect : ansisql.ANSIDialect, sa.engine.default.DefaultDialect: ansisql.ANSIDialect,
sa.databases.sqlite.SQLiteDialect : sqlite.SQLiteDialect, sa.databases.sqlite.SQLiteDialect: sqlite.SQLiteDialect,
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,
} }
def get_engine_visitor(engine,name):
return get_dialect_visitor(engine.dialect,name)
def get_dialect_visitor(sa_dialect,name): def get_engine_visitor(engine, name):
"""
Get the visitor implementation for the given database engine.
"""
return get_dialect_visitor(engine.dialect, name)
def get_dialect_visitor(sa_dialect, name):
"""
Get the visitor implementation for the given dialect.
Finds the visitor implementation based on the dialect class and
returns and instance initialized with the given name.
"""
sa_dialect_cls = sa_dialect.__class__ sa_dialect_cls = sa_dialect.__class__
migrate_dialect_cls = dialects[sa_dialect_cls] migrate_dialect_cls = dialects[sa_dialect_cls]
return migrate_dialect_cls.visitor(name) return migrate_dialect_cls.visitor(name)