Changeset - 0ccdb52079e9
[Not reviewed]
default
0 1 0
timeless@gmail.com - 10 years ago 2016-05-03 14:12:24
timeless@gmail.com
spelling: referred
1 file changed with 1 insertions and 1 deletions:
0 comments (0 inline, 0 general)
kallithea/lib/dbmigrate/migrate/changeset/schema.py
Show inline comments
 
"""
 
   Schema module providing common schema operations.
 
"""
 
import warnings
 

	
 
from UserDict import DictMixin
 

	
 
import sqlalchemy
 

	
 
from sqlalchemy.schema import ForeignKeyConstraint
 
from sqlalchemy.schema import UniqueConstraint
 

	
 
from kallithea.lib.dbmigrate.migrate.exceptions import *
 
from kallithea.lib.dbmigrate.migrate.changeset import SQLA_06, SQLA_07
 
from kallithea.lib.dbmigrate.migrate.changeset.databases.visitor import (get_engine_visitor,
 
                                                                         run_single_visitor)
 

	
 

	
 
__all__ = [
 
    'create_column',
 
    'drop_column',
 
    'alter_column',
 
    'rename_table',
 
    'rename_index',
 
    'ChangesetTable',
 
    'ChangesetColumn',
 
    'ChangesetIndex',
 
    'ChangesetDefaultClause',
 
    'ColumnDelta',
 
]
 

	
 
def create_column(column, table=None, *p, **kw):
 
    """Create a column, given the table.
 

	
 
    API to :meth:`ChangesetColumn.create`.
 
    """
 
    if table is not None:
 
        return table.create_column(column, *p, **kw)
 
    return column.create(*p, **kw)
 

	
 

	
 
def drop_column(column, table=None, *p, **kw):
 
    """Drop a column, given the table.
 

	
 
    API to :meth:`ChangesetColumn.drop`.
 
    """
 
    if table is not None:
 
        return table.drop_column(column, *p, **kw)
 
    return column.drop(*p, **kw)
 

	
 

	
 
def rename_table(table, name, engine=None, **kw):
 
    """Rename a table.
 

	
 
    If Table instance is given, engine is not used.
 

	
 
    API to :meth:`ChangesetTable.rename`.
 

	
 
    :param table: Table to be renamed.
 
    :param name: New name for Table.
 
    :param engine: Engine instance.
 
    :type table: string or Table instance
 
    :type name: string
 
    :type engine: obj
 
    """
 
    table = _to_table(table, engine)
 
    table.rename(name, **kw)
 

	
 

	
 
def rename_index(index, name, table=None, engine=None, **kw):
 
    """Rename an index.
 

	
 
    If Index instance is given,
 
    table and engine are not used.
 

	
 
    API to :meth:`ChangesetIndex.rename`.
 

	
 
    :param index: Index to be renamed.
 
    :param name: New name for index.
 
    :param table: Table to which Index is reffered.
 
    :param table: Table to which Index is referred.
 
    :param engine: Engine instance.
 
    :type index: string or Index instance
 
    :type name: string
 
    :type table: string or Table instance
 
    :type engine: obj
 
    """
 
    index = _to_index(index, table, engine)
 
    index.rename(name, **kw)
 

	
 

	
 
def alter_column(*p, **k):
 
    """Alter a column.
 

	
 
    This is a helper function that creates a :class:`ColumnDelta` and
 
    runs it.
 

	
 
    :argument column:
 
      The name of the column to be altered or a
 
      :class:`ChangesetColumn` column representing it.
 

	
 
    :param table:
 
      A :class:`~sqlalchemy.schema.Table` or table name to
 
      for the table where the column will be changed.
 

	
 
    :param engine:
 
      The :class:`~sqlalchemy.engine.base.Engine` to use for table
 
      reflection and schema alterations.
 

	
 
    :returns: A :class:`ColumnDelta` instance representing the change.
 

	
 

	
 
    """
 

	
 
    if 'table' not in k and isinstance(p[0], sqlalchemy.Column):
 
        k['table'] = p[0].table
 
    if 'engine' not in k:
 
        k['engine'] = k['table'].bind
 

	
 
    # deprecation
 
    if len(p) >= 2 and isinstance(p[1], sqlalchemy.Column):
 
        warnings.warn(
 
            "Passing a Column object to alter_column is deprecated."
 
            " Just pass in keyword parameters instead.",
 
            MigrateDeprecationWarning
 
            )
 
    engine = k['engine']
 

	
 
    # enough tests seem to break when metadata is always altered
 
    # that this crutch has to be left in until they can be sorted
 
    # out
 
    k['alter_metadata']=True
 

	
 
    delta = ColumnDelta(*p, **k)
 

	
 
    visitorcallable = get_engine_visitor(engine, 'schemachanger')
 
    engine._run_visitor(visitorcallable, delta)
 

	
 
    return delta
 

	
 

	
 
def _to_table(table, engine=None):
 
    """Return if instance of Table, else construct new with metadata"""
 
    if isinstance(table, sqlalchemy.Table):
 
        return table
 

	
 
    # Given: table name, maybe an engine
 
    meta = sqlalchemy.MetaData()
 
    if engine is not None:
 
        meta.bind = engine
 
    return sqlalchemy.Table(table, meta)
 

	
 

	
 
def _to_index(index, table=None, engine=None):
 
    """Return if instance of Index, else construct new with metadata"""
 
    if isinstance(index, sqlalchemy.Index):
 
        return index
 

	
 
    # Given: index name; table name required
 
    table = _to_table(table, engine)
 
    ret = sqlalchemy.Index(index)
 
    ret.table = table
 
    return ret
 

	
 

	
 
class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem):
 
    """Extracts the differences between two columns/column-parameters
 

	
 
        May receive parameters arranged in several different ways:
 

	
 
        * **current_column, new_column, \*p, \*\*kw**
 
            Additional parameters can be specified to override column
 
            differences.
 

	
 
        * **current_column, \*p, \*\*kw**
 
            Additional parameters alter current_column. Table name is extracted
 
            from current_column object.
 
            Name is changed to current_column.name from current_name,
 
            if current_name is specified.
 

	
 
        * **current_col_name, \*p, \*\*kw**
 
            Table kw must specified.
 

	
 
        :param table: Table at which current Column should be bound to. \
 
        If table name is given, reflection will be used.
 
        :type table: string or Table instance
 

	
 
        :param metadata: A :class:`MetaData` instance to store
 
                         reflected table names
 

	
 
        :param engine: When reflecting tables, either engine or metadata must \
 
        be specified to acquire engine object.
 
        :type engine: :class:`Engine` instance
 
        :returns: :class:`ColumnDelta` instance provides interface for altered attributes to \
 
        `result_column` through :func:`dict` alike object.
 

	
 
        * :class:`ColumnDelta`.result_column is altered column with new attributes
 

	
 
        * :class:`ColumnDelta`.current_name is current name of column in db
 

	
 

	
 
    """
 

	
 
    # Column attributes that can be altered
 
    diff_keys = ('name', 'type', 'primary_key', 'nullable',
 
        'server_onupdate', 'server_default', 'autoincrement')
 
    diffs = dict()
 
    __visit_name__ = 'column'
 

	
 
    def __init__(self, *p, **kw):
 
        # 'alter_metadata' is not a public api. It exists purely
 
        # as a crutch until the tests that fail when 'alter_metadata'
 
        # behaviour always happens can be sorted out
 
        self.alter_metadata = kw.pop("alter_metadata", False)
 

	
 
        self.meta = kw.pop("metadata", None)
 
        self.engine = kw.pop("engine", None)
 

	
 
        # Things are initialized differently depending on how many column
 
        # parameters are given. Figure out how many and call the appropriate
 
        # method.
 
        if len(p) >= 1 and isinstance(p[0], sqlalchemy.Column):
 
            # At least one column specified
 
            if len(p) >= 2 and isinstance(p[1], sqlalchemy.Column):
 
                # Two columns specified
 
                diffs = self.compare_2_columns(*p, **kw)
 
            else:
 
                # Exactly one column specified
 
                diffs = self.compare_1_column(*p, **kw)
 
        else:
 
            # Zero columns specified
 
            if not len(p) or not isinstance(p[0], basestring):
 
                raise ValueError("First argument must be column name")
 
            diffs = self.compare_parameters(*p, **kw)
 

	
 
        self.apply_diffs(diffs)
 

	
 
    def __repr__(self):
 
        return '<ColumnDelta altermetadata=%r, %s>' % (
 
            self.alter_metadata,
 
            super(ColumnDelta, self).__repr__()
 
            )
 

	
 
    def __getitem__(self, key):
 
        if key not in self.keys():
 
            raise KeyError("No such diff key, available: %s" % self.diffs )
 
        return getattr(self.result_column, key)
 

	
 
    def __setitem__(self, key, value):
 
        if key not in self.keys():
 
            raise KeyError("No such diff key, available: %s" % self.diffs )
 
        setattr(self.result_column, key, value)
 

	
 
    def __delitem__(self, key):
 
        raise NotImplementedError
 

	
 
    def keys(self):
 
        return self.diffs.keys()
 

	
 
    def compare_parameters(self, current_name, *p, **k):
 
        """Compares Column objects with reflection"""
 
        self.table = k.pop('table')
 
        self.result_column = self._table.c.get(current_name)
 
        if len(p):
 
            k = self._extract_parameters(p, k, self.result_column)
 
        return k
 

	
 
    def compare_1_column(self, col, *p, **k):
 
        """Compares one Column object"""
 
        self.table = k.pop('table', None)
 
        if self.table is None:
 
            self.table = col.table
 
        self.result_column = col
0 comments (0 inline, 0 general)