diff options
author | Jim Rollenhagen <jim@jimrollenhagen.com> | 2019-09-26 09:56:42 -0400 |
---|---|---|
committer | Jim Rollenhagen <jim@jimrollenhagen.com> | 2019-09-26 09:56:42 -0400 |
commit | 52672a64cc0cab4ea14a4a756fce850eb03315e3 (patch) | |
tree | a86024e4e6141aa8983c750f751c58d924f5b11a /migrate/changeset/databases | |
parent | 8acab2cd75a5b23ac162e49c8e4fb1e3f958352a (diff) | |
download | sqlalchemy-migrate-master.tar.gz |
Diffstat (limited to 'migrate/changeset/databases')
-rw-r--r-- | migrate/changeset/databases/__init__.py | 11 | ||||
-rw-r--r-- | migrate/changeset/databases/firebird.py | 93 | ||||
-rw-r--r-- | migrate/changeset/databases/ibmdb2.py | 337 | ||||
-rw-r--r-- | migrate/changeset/databases/mysql.py | 68 | ||||
-rw-r--r-- | migrate/changeset/databases/oracle.py | 108 | ||||
-rw-r--r-- | migrate/changeset/databases/postgres.py | 42 | ||||
-rw-r--r-- | migrate/changeset/databases/sqlite.py | 229 | ||||
-rw-r--r-- | migrate/changeset/databases/visitor.py | 88 |
8 files changed, 0 insertions, 976 deletions
diff --git a/migrate/changeset/databases/__init__.py b/migrate/changeset/databases/__init__.py deleted file mode 100644 index 075a787..0000000 --- a/migrate/changeset/databases/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -""" - This module contains database dialect specific changeset - implementations. -""" -__all__ = [ - 'postgres', - 'sqlite', - 'mysql', - 'oracle', - 'ibmdb2', -] diff --git a/migrate/changeset/databases/firebird.py b/migrate/changeset/databases/firebird.py deleted file mode 100644 index 0f16b0a..0000000 --- a/migrate/changeset/databases/firebird.py +++ /dev/null @@ -1,93 +0,0 @@ -""" - Firebird database specific implementations of changeset classes. -""" -from sqlalchemy.databases import firebird as sa_base -from sqlalchemy.schema import PrimaryKeyConstraint -from migrate import exceptions -from migrate.changeset import ansisql - - -FBSchemaGenerator = sa_base.FBDDLCompiler - -class FBColumnGenerator(FBSchemaGenerator, ansisql.ANSIColumnGenerator): - """Firebird column generator implementation.""" - - -class FBColumnDropper(ansisql.ANSIColumnDropper): - """Firebird column dropper implementation.""" - - def visit_column(self, column): - """Firebird supports 'DROP col' instead of 'DROP COLUMN col' syntax - - Drop primary key and unique constraints if dropped column is referencing it.""" - if column.primary_key: - if column.table.primary_key.columns.contains_column(column): - column.table.primary_key.drop() - # TODO: recreate primary key if it references more than this column - - for index in column.table.indexes: - # "column in index.columns" causes problems as all - # column objects compare equal and return a SQL expression - if column.name in [col.name for col in index.columns]: - index.drop() - # TODO: recreate index if it references more than this column - - for cons in column.table.constraints: - if isinstance(cons,PrimaryKeyConstraint): - # will be deleted only when the column its on - # is deleted! - continue - - should_drop = column.name in cons.columns - if should_drop: - self.start_alter_table(column) - self.append("DROP CONSTRAINT ") - self.append(self.preparer.format_constraint(cons)) - self.execute() - # TODO: recreate unique constraint if it refenrences more than this column - - 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, column, delta): - self.start_alter_table(table) - col_name = self.preparer.quote(delta.current_name) - new_name = self.preparer.format_column(delta.result_column) - self.append('ALTER COLUMN %s TO %s' % (col_name, new_name)) - - def _visit_column_nullable(self, table, column, 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): - """Cascading constraints is not supported""" - raise exceptions.NotSupportedError( - "Firebird does not support cascading constraints") - - -class FBDialect(ansisql.ANSIDialect): - columngenerator = FBColumnGenerator - columndropper = FBColumnDropper - schemachanger = FBSchemaChanger - constraintgenerator = FBConstraintGenerator - constraintdropper = FBConstraintDropper diff --git a/migrate/changeset/databases/ibmdb2.py b/migrate/changeset/databases/ibmdb2.py deleted file mode 100644 index a12d73b..0000000 --- a/migrate/changeset/databases/ibmdb2.py +++ /dev/null @@ -1,337 +0,0 @@ -""" - DB2 database specific implementations of changeset classes. -""" - -import logging - -from ibm_db_sa import base -from sqlalchemy.schema import (AddConstraint, - CreateIndex, - DropConstraint) -from sqlalchemy.schema import (Index, - PrimaryKeyConstraint, - UniqueConstraint) - -from migrate.changeset import ansisql -from migrate.changeset import constraint -from migrate.changeset import util -from migrate import exceptions - - -LOG = logging.getLogger(__name__) - -IBMDBSchemaGenerator = base.IBM_DBDDLCompiler - - -def get_server_version_info(dialect): - """Returns the DB2 server major and minor version as a list of ints.""" - return [int(ver_token) for ver_token in dialect.dbms_ver.split('.')[0:2]] - - -def is_unique_constraint_with_null_columns_supported(dialect): - """Checks to see if the DB2 version is at least 10.5. - - This is needed for checking if unique constraints with null columns - are supported. - """ - return get_server_version_info(dialect) >= [10, 5] - - -class IBMDBColumnGenerator(IBMDBSchemaGenerator, - ansisql.ANSIColumnGenerator): - def visit_column(self, column): - nullable = True - if not column.nullable: - nullable = False - column.nullable = True - - table = self.start_alter_table(column) - self.append("ADD COLUMN ") - self.append(self.get_column_specification(column)) - - for cons in column.constraints: - self.traverse_single(cons) - if column.default is not None: - self.traverse_single(column.default) - self.execute() - - #ALTER TABLE STATEMENTS - if not nullable: - self.start_alter_table(column) - self.append("ALTER COLUMN %s SET NOT NULL" % - self.preparer.format_column(column)) - self.execute() - self.append("CALL SYSPROC.ADMIN_CMD('REORG TABLE %s')" % - self.preparer.format_table(table)) - self.execute() - - # add indexes and unique constraints - if column.index_name: - Index(column.index_name, column).create() - elif column.unique_name: - constraint.UniqueConstraint(column, - name=column.unique_name).create() - - # SA bounds FK constraints to table, add manually - for fk in column.foreign_keys: - self.add_foreignkey(fk.constraint) - - # add primary key constraint if needed - if column.primary_key_name: - pk = constraint.PrimaryKeyConstraint( - column, name=column.primary_key_name) - pk.create() - - self.append("COMMIT") - self.execute() - self.append("CALL SYSPROC.ADMIN_CMD('REORG TABLE %s')" % - self.preparer.format_table(table)) - self.execute() - - -class IBMDBColumnDropper(ansisql.ANSIColumnDropper): - def visit_column(self, column): - """Drop a column from its table. - - :param column: the column object - :type column: :class:`sqlalchemy.Column` - """ - #table = self.start_alter_table(column) - super(IBMDBColumnDropper, self).visit_column(column) - self.append("CALL SYSPROC.ADMIN_CMD('REORG TABLE %s')" % - self.preparer.format_table(column.table)) - self.execute() - - -class IBMDBSchemaChanger(IBMDBSchemaGenerator, ansisql.ANSISchemaChanger): - def visit_table(self, table): - """Rename a table; #38. Other ops aren't supported.""" - - self._rename_table(table) - self.append("TO %s" % self.preparer.quote(table.new_name)) - self.execute() - self.append("COMMIT") - self.execute() - - def _rename_table(self, table): - self.append("RENAME TABLE %s " % self.preparer.format_table(table)) - - def visit_index(self, index): - if hasattr(self, '_index_identifier'): - # SA >= 0.6.5, < 0.8 - old_name = self.preparer.quote( - self._index_identifier(index.name)) - new_name = self.preparer.quote( - self._index_identifier(index.new_name)) - else: - # SA >= 0.8 - class NewName(object): - """Map obj.name -> obj.new_name""" - def __init__(self, index): - self.name = index.new_name - self._obj = index - - def __getattr__(self, attr): - if attr == 'name': - return getattr(self, attr) - return getattr(self._obj, attr) - - old_name = self._prepared_index_name(index) - new_name = self._prepared_index_name(NewName(index)) - - self.append("RENAME INDEX %s TO %s" % (old_name, new_name)) - self.execute() - self.append("COMMIT") - self.execute() - - def _run_subvisit(self, delta, func, start_alter=True): - """Runs visit method based on what needs to be changed on column""" - table = delta.table - if start_alter: - self.start_alter_table(table) - ret = func(table, - self.preparer.quote(delta.current_name), - delta) - self.execute() - self._reorg_table(self.preparer.format_table(delta.table)) - - def _reorg_table(self, delta): - self.append("CALL SYSPROC.ADMIN_CMD('REORG TABLE %s')" % delta) - self.execute() - - def visit_column(self, delta): - keys = delta.keys() - tr = self.connection.begin() - column = delta.result_column.copy() - - if 'type' in keys: - try: - self._run_subvisit(delta, self._visit_column_change, False) - except Exception as e: - LOG.warn("Unable to change the column type. Error: %s" % e) - - if column.primary_key and 'primary_key' not in keys: - try: - self._run_subvisit(delta, self._visit_primary_key) - except Exception as e: - LOG.warn("Unable to add primary key. Error: %s" % e) - - if 'nullable' in keys: - self._run_subvisit(delta, self._visit_column_nullable) - - if 'server_default' in keys: - self._run_subvisit(delta, self._visit_column_default) - - if 'primary_key' in keys: - self._run_subvisit(delta, self._visit_primary_key) - self._run_subvisit(delta, self._visit_unique_constraint) - - if 'name' in keys: - try: - self._run_subvisit(delta, self._visit_column_name, False) - except Exception as e: - LOG.warn("Unable to change column %(name)s. Error: %(error)s" % - {'name': delta.current_name, 'error': e}) - - self._reorg_table(self.preparer.format_table(delta.table)) - self.append("COMMIT") - self.execute() - tr.commit() - - def _visit_unique_constraint(self, table, col_name, delta): - # Add primary key to the current column - self.append("ADD CONSTRAINT %s " % col_name) - self.append("UNIQUE (%s)" % col_name) - - def _visit_primary_key(self, table, col_name, delta): - # Add primary key to the current column - self.append("ADD PRIMARY KEY (%s)" % col_name) - - def _visit_column_name(self, table, col_name, delta): - column = delta.result_column.copy() - - # Delete the primary key before renaming the column - if column.primary_key: - try: - self.start_alter_table(table) - self.append("DROP PRIMARY KEY") - self.execute() - except Exception: - LOG.debug("Continue since Primary key does not exist.") - - self.start_alter_table(table) - new_name = self.preparer.format_column(delta.result_column) - self.append("RENAME COLUMN %s TO %s" % (col_name, new_name)) - - if column.primary_key: - # execute the rename before adding primary key back - self.execute() - self.start_alter_table(table) - self.append("ADD PRIMARY KEY (%s)" % new_name) - - def _visit_column_nullable(self, table, col_name, delta): - self.append("ALTER COLUMN %s " % col_name) - nullable = delta['nullable'] - if nullable: - self.append("DROP NOT NULL") - else: - self.append("SET NOT NULL") - - def _visit_column_default(self, table, col_name, delta): - default_text = self.get_column_default_string(delta.result_column) - self.append("ALTER COLUMN %s " % col_name) - if default_text is None: - self.append("DROP DEFAULT") - else: - self.append("SET WITH DEFAULT %s" % default_text) - - def _visit_column_change(self, table, col_name, delta): - column = delta.result_column.copy() - - # Delete the primary key before - if column.primary_key: - try: - self.start_alter_table(table) - self.append("DROP PRIMARY KEY") - self.execute() - except Exception: - LOG.debug("Continue since Primary key does not exist.") - # Delete the identity before - try: - self.start_alter_table(table) - self.append("ALTER COLUMN %s DROP IDENTITY" % col_name) - self.execute() - except Exception: - LOG.debug("Continue since identity does not exist.") - - column.default = None - if not column.table: - column.table = delta.table - self.start_alter_table(table) - self.append("ALTER COLUMN %s " % col_name) - self.append("SET DATA TYPE ") - type_text = self.dialect.type_compiler.process( - delta.result_column.type) - self.append(type_text) - - -class IBMDBConstraintGenerator(ansisql.ANSIConstraintGenerator): - def _visit_constraint(self, constraint): - constraint.name = self.get_constraint_name(constraint) - if (isinstance(constraint, UniqueConstraint) and - is_unique_constraint_with_null_columns_supported( - self.dialect)): - for column in constraint: - if column.nullable: - constraint.exclude_nulls = True - break - if getattr(constraint, 'exclude_nulls', None): - index = Index(constraint.name, - *(column for column in constraint), - unique=True) - sql = self.process(CreateIndex(index)) - sql += ' EXCLUDE NULL KEYS' - else: - sql = self.process(AddConstraint(constraint)) - self.append(sql) - self.execute() - - -class IBMDBConstraintDropper(ansisql.ANSIConstraintDropper, - ansisql.ANSIConstraintCommon): - def _visit_constraint(self, constraint): - constraint.name = self.get_constraint_name(constraint) - if (isinstance(constraint, UniqueConstraint) and - is_unique_constraint_with_null_columns_supported( - self.dialect)): - for column in constraint: - if column.nullable: - constraint.exclude_nulls = True - break - if getattr(constraint, 'exclude_nulls', None): - if hasattr(self, '_index_identifier'): - # SA >= 0.6.5, < 0.8 - index_name = self.preparer.quote( - self._index_identifier(constraint.name)) - else: - # SA >= 0.8 - index_name = self._prepared_index_name(constraint) - sql = 'DROP INDEX %s ' % index_name - else: - sql = self.process(DropConstraint(constraint, - cascade=constraint.cascade)) - self.append(sql) - self.execute() - - def visit_migrate_primary_key_constraint(self, constraint): - self.start_alter_table(constraint.table) - self.append("DROP PRIMARY KEY") - self.execute() - - -class IBMDBDialect(ansisql.ANSIDialect): - columngenerator = IBMDBColumnGenerator - columndropper = IBMDBColumnDropper - schemachanger = IBMDBSchemaChanger - constraintgenerator = IBMDBConstraintGenerator - constraintdropper = IBMDBConstraintDropper diff --git a/migrate/changeset/databases/mysql.py b/migrate/changeset/databases/mysql.py deleted file mode 100644 index 1c01706..0000000 --- a/migrate/changeset/databases/mysql.py +++ /dev/null @@ -1,68 +0,0 @@ -""" - MySQL database specific implementations of changeset classes. -""" - -import sqlalchemy -from sqlalchemy.databases import mysql as sa_base -from sqlalchemy import types as sqltypes - -from migrate import exceptions -from migrate.changeset import ansisql -from migrate.changeset import util - - - -MySQLSchemaGenerator = sa_base.MySQLDDLCompiler - -class MySQLColumnGenerator(MySQLSchemaGenerator, ansisql.ANSIColumnGenerator): - pass - - -class MySQLColumnDropper(ansisql.ANSIColumnDropper): - pass - - -class MySQLSchemaChanger(MySQLSchemaGenerator, ansisql.ANSISchemaChanger): - - def visit_column(self, delta): - table = delta.table - colspec = self.get_column_specification(delta.result_column) - if delta.result_column.autoincrement: - primary_keys = [c for c in table.primary_key.columns - if (c.autoincrement and - isinstance(c.type, sqltypes.Integer) and - not c.foreign_keys)] - - if primary_keys: - first = primary_keys.pop(0) - if first.name == delta.current_name: - colspec += " AUTO_INCREMENT" - old_col_name = self.preparer.quote(delta.current_name) - - self.start_alter_table(table) - - self.append("CHANGE COLUMN %s " % old_col_name) - self.append(colspec) - self.execute() - - def visit_index(self, param): - # If MySQL can do this, I can't find how - raise exceptions.NotSupportedError("MySQL cannot rename indexes") - - -class MySQLConstraintGenerator(ansisql.ANSIConstraintGenerator): - pass - - -class MySQLConstraintDropper(MySQLSchemaGenerator, ansisql.ANSIConstraintDropper): - def visit_migrate_check_constraint(self, *p, **k): - raise exceptions.NotSupportedError("MySQL does not support CHECK" - " constraints, use triggers instead.") - - -class MySQLDialect(ansisql.ANSIDialect): - columngenerator = MySQLColumnGenerator - columndropper = MySQLColumnDropper - schemachanger = MySQLSchemaChanger - constraintgenerator = MySQLConstraintGenerator - constraintdropper = MySQLConstraintDropper diff --git a/migrate/changeset/databases/oracle.py b/migrate/changeset/databases/oracle.py deleted file mode 100644 index 2f16b5b..0000000 --- a/migrate/changeset/databases/oracle.py +++ /dev/null @@ -1,108 +0,0 @@ -""" - Oracle database specific implementations of changeset classes. -""" -import sqlalchemy as sa -from sqlalchemy.databases import oracle as sa_base - -from migrate import exceptions -from migrate.changeset import ansisql - - -OracleSchemaGenerator = sa_base.OracleDDLCompiler - - -class OracleColumnGenerator(OracleSchemaGenerator, ansisql.ANSIColumnGenerator): - pass - - -class OracleColumnDropper(ansisql.ANSIColumnDropper): - pass - - -class OracleSchemaChanger(OracleSchemaGenerator, ansisql.ANSISchemaChanger): - - def get_column_specification(self, column, **kwargs): - # Ignore the NOT NULL generated - override_nullable = kwargs.pop('override_nullable', None) - if override_nullable: - orig = column.nullable - column.nullable = True - ret = super(OracleSchemaChanger, self).get_column_specification( - column, **kwargs) - if override_nullable: - column.nullable = orig - return ret - - def visit_column(self, delta): - keys = delta.keys() - - if 'name' in keys: - self._run_subvisit(delta, - self._visit_column_name, - start_alter=False) - - if len(set(('type', 'nullable', 'server_default')).intersection(keys)): - self._run_subvisit(delta, - self._visit_column_change, - start_alter=False) - - def _visit_column_change(self, table, column, delta): - # Oracle cannot drop a default once created, but it can set it - # to null. We'll do that if default=None - # http://forums.oracle.com/forums/message.jspa?messageID=1273234#1273234 - dropdefault_hack = (column.server_default is None \ - and 'server_default' in delta.keys()) - # Oracle apparently doesn't like it when we say "not null" if - # 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())) - - if dropdefault_hack: - column.server_default = sa.PassiveDefault(sa.sql.null()) - if notnull_hack: - column.nullable = True - colspec = self.get_column_specification(column, - override_nullable=null_hack) - if null_hack: - colspec += ' NULL' - if notnull_hack: - column.nullable = False - if dropdefault_hack: - column.server_default = None - - self.start_alter_table(table) - self.append("MODIFY (") - self.append(colspec) - self.append(")") - - -class OracleConstraintCommon(object): - - def get_constraint_name(self, cons): - # Oracle constraints can't guess their name like other DBs - if not cons.name: - raise exceptions.NotSupportedError( - "Oracle constraint names must be explicitly stated") - return cons.name - - -class OracleConstraintGenerator(OracleConstraintCommon, - ansisql.ANSIConstraintGenerator): - pass - - -class OracleConstraintDropper(OracleConstraintCommon, - ansisql.ANSIConstraintDropper): - pass - - -class OracleDialect(ansisql.ANSIDialect): - columngenerator = OracleColumnGenerator - columndropper = OracleColumnDropper - schemachanger = OracleSchemaChanger - constraintgenerator = OracleConstraintGenerator - constraintdropper = OracleConstraintDropper diff --git a/migrate/changeset/databases/postgres.py b/migrate/changeset/databases/postgres.py deleted file mode 100644 index 10ea094..0000000 --- a/migrate/changeset/databases/postgres.py +++ /dev/null @@ -1,42 +0,0 @@ -""" - `PostgreSQL`_ database specific implementations of changeset classes. - - .. _`PostgreSQL`: http://www.postgresql.org/ -""" -from migrate.changeset import ansisql - -from sqlalchemy.databases import postgresql as sa_base -PGSchemaGenerator = sa_base.PGDDLCompiler - - -class PGColumnGenerator(PGSchemaGenerator, ansisql.ANSIColumnGenerator): - """PostgreSQL column generator implementation.""" - pass - - -class PGColumnDropper(ansisql.ANSIColumnDropper): - """PostgreSQL column dropper implementation.""" - pass - - -class PGSchemaChanger(ansisql.ANSISchemaChanger): - """PostgreSQL schema changer implementation.""" - pass - - -class PGConstraintGenerator(ansisql.ANSIConstraintGenerator): - """PostgreSQL constraint generator implementation.""" - pass - - -class PGConstraintDropper(ansisql.ANSIConstraintDropper): - """PostgreSQL constaint dropper implementation.""" - pass - - -class PGDialect(ansisql.ANSIDialect): - columngenerator = PGColumnGenerator - columndropper = PGColumnDropper - schemachanger = PGSchemaChanger - constraintgenerator = PGConstraintGenerator - constraintdropper = PGConstraintDropper diff --git a/migrate/changeset/databases/sqlite.py b/migrate/changeset/databases/sqlite.py deleted file mode 100644 index 908c800..0000000 --- a/migrate/changeset/databases/sqlite.py +++ /dev/null @@ -1,229 +0,0 @@ -""" - `SQLite`_ database specific implementations of changeset classes. - - .. _`SQLite`: http://www.sqlite.org/ -""" -try: # Python 3 - from collections.abc import MutableMapping as DictMixin -except ImportError: # Python 2 - from UserDict import DictMixin -from copy import copy -import re - -from sqlalchemy.databases import sqlite as sa_base -from sqlalchemy.schema import ForeignKeyConstraint -from sqlalchemy.schema import UniqueConstraint - -from migrate import exceptions -from migrate.changeset import ansisql - - -SQLiteSchemaGenerator = sa_base.SQLiteDDLCompiler - - -class SQLiteCommon(object): - - def _not_supported(self, op): - raise exceptions.NotSupportedError("SQLite does not support " - "%s; see http://www.sqlite.org/lang_altertable.html" % op) - - -class SQLiteHelper(SQLiteCommon): - - def _filter_columns(self, cols, table): - """Splits the string of columns and returns those only in the table. - - :param cols: comma-delimited string of table columns - :param table: the table to check - :return: list of columns in the table - """ - columns = [] - for c in cols.split(","): - if c in table.columns: - # There was a bug in reflection of SQLite columns with - # reserved identifiers as names (SQLite can return them - # wrapped with double quotes), so strip double quotes. - columns.extend(c.strip(' "')) - return columns - - def _get_constraints(self, table): - """Retrieve information about existing constraints of the table - - This feature is needed for recreate_table() to work properly. - """ - - data = table.metadata.bind.execute( - """SELECT sql - FROM sqlite_master - WHERE - type='table' AND - name=:table_name""", - table_name=table.name - ).fetchone()[0] - - UNIQUE_PATTERN = "CONSTRAINT (\w+) UNIQUE \(([^\)]+)\)" - constraints = [] - for name, cols in re.findall(UNIQUE_PATTERN, data): - # Filter out any columns that were dropped from the table. - columns = self._filter_columns(cols, table) - if columns: - constraints.extend(UniqueConstraint(*columns, name=name)) - - FKEY_PATTERN = "CONSTRAINT (\w+) FOREIGN KEY \(([^\)]+)\)" - for name, cols in re.findall(FKEY_PATTERN, data): - # Filter out any columns that were dropped from the table. - columns = self._filter_columns(cols, table) - if columns: - constraints.extend(ForeignKeyConstraint(*columns, name=name)) - - return constraints - - def recreate_table(self, table, column=None, delta=None, - omit_constraints=None): - table_name = self.preparer.format_table(table) - - # we remove all indexes so as not to have - # problems during copy and re-create - for index in table.indexes: - index.drop() - - # reflect existing constraints - for constraint in self._get_constraints(table): - table.append_constraint(constraint) - # omit given constraints when creating a new table if required - table.constraints = set([ - cons for cons in table.constraints - if omit_constraints is None or cons.name not in omit_constraints - ]) - - # Use "PRAGMA legacy_alter_table = ON" with sqlite >= 3.26 when - # using "ALTER TABLE RENAME TO migration_tmp" to maintain legacy - # behavior. See: https://www.sqlite.org/src/info/ae9638e9c0ad0c36 - if self.connection.engine.dialect.server_version_info >= (3, 26): - self.append('PRAGMA legacy_alter_table = ON') - self.execute() - self.append('ALTER TABLE %s RENAME TO migration_tmp' % table_name) - self.execute() - if self.connection.engine.dialect.server_version_info >= (3, 26): - self.append('PRAGMA legacy_alter_table = OFF') - self.execute() - - insertion_string = self._modify_table(table, column, delta) - - table.create(bind=self.connection) - self.append(insertion_string % {'table_name': table_name}) - self.execute() - self.append('DROP TABLE migration_tmp') - self.execute() - - def visit_column(self, delta): - if isinstance(delta, DictMixin): - column = delta.result_column - table = self._to_table(delta.table) - else: - column = delta - table = self._to_table(column.table) - self.recreate_table(table,column,delta) - -class SQLiteColumnGenerator(SQLiteSchemaGenerator, - ansisql.ANSIColumnGenerator, - # at the end so we get the normal - # visit_column by default - SQLiteHelper, - SQLiteCommon - ): - """SQLite ColumnGenerator""" - - def _modify_table(self, table, column, delta): - columns = ' ,'.join(map( - self.preparer.format_column, - [c for c in table.columns if c.name!=column.name])) - return ('INSERT INTO %%(table_name)s (%(cols)s) ' - 'SELECT %(cols)s from migration_tmp')%{'cols':columns} - - def visit_column(self,column): - if column.foreign_keys: - SQLiteHelper.visit_column(self,column) - else: - super(SQLiteColumnGenerator,self).visit_column(column) - -class SQLiteColumnDropper(SQLiteHelper, ansisql.ANSIColumnDropper): - """SQLite ColumnDropper""" - - def _modify_table(self, table, column, delta): - - columns = ' ,'.join(map(self.preparer.format_column, table.columns)) - return 'INSERT INTO %(table_name)s SELECT ' + columns + \ - ' from migration_tmp' - - def visit_column(self,column): - # For SQLite, we *have* to remove the column here so the table - # is re-created properly. - column.remove_from_table(column.table,unset_table=False) - super(SQLiteColumnDropper,self).visit_column(column) - - -class SQLiteSchemaChanger(SQLiteHelper, ansisql.ANSISchemaChanger): - """SQLite SchemaChanger""" - - def _modify_table(self, table, column, delta): - return 'INSERT INTO %(table_name)s SELECT * from migration_tmp' - - def visit_index(self, index): - """Does not support ALTER INDEX""" - self._not_supported('ALTER INDEX') - - -class SQLiteConstraintGenerator(ansisql.ANSIConstraintGenerator, SQLiteHelper, SQLiteCommon): - - def visit_migrate_primary_key_constraint(self, constraint): - tmpl = "CREATE UNIQUE INDEX %s ON %s ( %s )" - cols = ', '.join(map(self.preparer.format_column, constraint.columns)) - tname = self.preparer.format_table(constraint.table) - name = self.get_constraint_name(constraint) - msg = tmpl % (name, tname, cols) - self.append(msg) - self.execute() - - def _modify_table(self, table, column, delta): - return 'INSERT INTO %(table_name)s SELECT * from migration_tmp' - - def visit_migrate_foreign_key_constraint(self, *p, **k): - self.recreate_table(p[0].table) - - def visit_migrate_unique_constraint(self, *p, **k): - self.recreate_table(p[0].table) - - -class SQLiteConstraintDropper(ansisql.ANSIColumnDropper, - SQLiteHelper, - ansisql.ANSIConstraintCommon): - - def _modify_table(self, table, column, delta): - return 'INSERT INTO %(table_name)s SELECT * from migration_tmp' - - def visit_migrate_primary_key_constraint(self, constraint): - tmpl = "DROP INDEX %s " - name = self.get_constraint_name(constraint) - msg = tmpl % (name) - self.append(msg) - self.execute() - - def visit_migrate_foreign_key_constraint(self, *p, **k): - self.recreate_table(p[0].table, omit_constraints=[p[0].name]) - - def visit_migrate_check_constraint(self, *p, **k): - self._not_supported('ALTER TABLE DROP CONSTRAINT') - - def visit_migrate_unique_constraint(self, *p, **k): - self.recreate_table(p[0].table, omit_constraints=[p[0].name]) - - -# TODO: technically primary key is a NOT NULL + UNIQUE constraint, should add NOT NULL to index - -class SQLiteDialect(ansisql.ANSIDialect): - columngenerator = SQLiteColumnGenerator - columndropper = SQLiteColumnDropper - schemachanger = SQLiteSchemaChanger - constraintgenerator = SQLiteConstraintGenerator - constraintdropper = SQLiteConstraintDropper diff --git a/migrate/changeset/databases/visitor.py b/migrate/changeset/databases/visitor.py deleted file mode 100644 index c70aa6b..0000000 --- a/migrate/changeset/databases/visitor.py +++ /dev/null @@ -1,88 +0,0 @@ -""" - Module for visitor class mapping. -""" -import sqlalchemy as sa - -from migrate.changeset import ansisql -from migrate.changeset.databases import (sqlite, - postgres, - mysql, - oracle, - firebird) - - -# Map SA dialects to the corresponding Migrate extensions -DIALECTS = { - "default": ansisql.ANSIDialect, - "sqlite": sqlite.SQLiteDialect, - "postgres": postgres.PGDialect, - "postgresql": postgres.PGDialect, - "mysql": mysql.MySQLDialect, - "oracle": oracle.OracleDialect, - "firebird": firebird.FBDialect, -} - - -# NOTE(mriedem): We have to conditionally check for DB2 in case ibm_db_sa -# isn't available since ibm_db_sa is not packaged in sqlalchemy like the -# other dialects. -try: - from migrate.changeset.databases import ibmdb2 - DIALECTS["ibm_db_sa"] = ibmdb2.IBMDBDialect -except ImportError: - pass - - -def get_engine_visitor(engine, name): - """ - Get the visitor implementation for the given database engine. - - :param engine: SQLAlchemy Engine - :param name: Name of the visitor - :type name: string - :type engine: Engine - :returns: visitor - """ - # TODO: link to supported visitors - 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. - - Binds dialect specific preparer to visitor. - """ - - # map sa dialect to migrate dialect and return visitor - sa_dialect_name = getattr(sa_dialect, 'name', 'default') - migrate_dialect_cls = DIALECTS[sa_dialect_name] - visitor = getattr(migrate_dialect_cls, name) - - # bind preparer - visitor.preparer = sa_dialect.preparer(sa_dialect) - - return visitor - -def run_single_visitor(engine, visitorcallable, element, - connection=None, **kwargs): - """Taken from :meth:`sqlalchemy.engine.base.Engine._run_single_visitor` - with support for migrate visitors. - """ - if connection is None: - conn = engine.contextual_connect(close_with_result=False) - else: - conn = connection - visitor = visitorcallable(engine.dialect, conn) - try: - if hasattr(element, '__migrate_visit_name__'): - fn = getattr(visitor, 'visit_' + element.__migrate_visit_name__) - else: - fn = getattr(visitor, 'visit_' + element.__visit_name__) - fn(element, **kwargs) - finally: - if connection is None: - conn.close() |