Esempio n. 1
0
def suspend_fk_constraints_for_col_alter(engine,
                                         table_name,
                                         column_name,
                                         referents=[]):
    """Detect foreign key constraints, drop, and recreate.

    This is used to guard against a column ALTER that on some backends
    cannot proceed unless foreign key constraints are not present.

    e.g.::

        from oslo_db.sqlalchemy.util import (
            suspend_fk_constraints_for_col_alter
        )

        with suspend_fk_constraints_for_col_alter(
            migrate_engine, "user_table",
            referents=[
                "local_user", "nonlocal_user", "project"
            ]):
            user_table.c.domain_id.alter(nullable=False)

    :param engine: a SQLAlchemy engine (or connection)

    :param table_name: target table name.  All foreign key constraints
     that refer to the table_name / column_name will be dropped and recreated.

    :param column_name: target column name.  all foreign key constraints
     which refer to this column, either partially or fully, will be dropped
     and recreated.

    :param referents: sequence of string table names to search for foreign
     key constraints.   A future version of this function may no longer
     require this argument, however for the moment it is required.

    """
    if (not ndb.ndb_status(engine)):
        yield
    else:
        with engine.connect() as conn:
            insp = inspect(conn)
            fks = []
            for ref_table_name in referents:
                for fk in insp.get_foreign_keys(ref_table_name):
                    if not fk.get('name'):
                        raise AssertionError("foreign key hasn't a name.")
                    if fk['referred_table'] == table_name and \
                            column_name in fk['referred_columns']:
                        fk['source_table'] = ref_table_name
                        if 'options' not in fk:
                            fk['options'] = {}
                        fks.append(fk)

            ctx = MigrationContext.configure(conn)
            op = Operations(ctx)

            for fk in fks:
                op.drop_constraint(fk['name'],
                                   fk['source_table'],
                                   type_="foreignkey")
            yield
            for fk in fks:
                op.create_foreign_key(
                    fk['name'],
                    fk['source_table'],
                    fk['referred_table'],
                    fk['constrained_columns'],
                    fk['referred_columns'],
                    onupdate=fk['options'].get('onupdate'),
                    ondelete=fk['options'].get('ondelete'),
                    deferrable=fk['options'].get('deferrable'),
                    initially=fk['options'].get('initially'),
                )
Esempio n. 2
0
def suspend_fk_constraints_for_col_alter(
        engine, table_name, column_name, referents=[]):
    """Detect foreign key constraints, drop, and recreate.

    This is used to guard against a column ALTER that on some backends
    cannot proceed unless foreign key constraints are not present.

    e.g.::

        from oslo_db.sqlalchemy.util import (
            suspend_fk_constraints_for_col_alter
        )

        with suspend_fk_constraints_for_col_alter(
            migrate_engine, "user_table",
            referents=[
                "local_user", "nonlocal_user", "project"
            ]):
            user_table.c.domain_id.alter(nullable=False)

    :param engine: a SQLAlchemy engine (or connection)

    :param table_name: target table name.  All foreign key constraints
     that refer to the table_name / column_name will be dropped and recreated.

    :param column_name: target column name.  all foreign key constraints
     which refer to this column, either partially or fully, will be dropped
     and recreated.

    :param referents: sequence of string table names to search for foreign
     key constraints.   A future version of this function may no longer
     require this argument, however for the moment it is required.

    """
    if (
        not ndb.ndb_status(engine)
    ):
        yield
    else:
        with engine.connect() as conn:
            insp = inspect(conn)
            fks = []
            for ref_table_name in referents:
                for fk in insp.get_foreign_keys(ref_table_name):
                    if not fk.get('name'):
                        raise AssertionError("foreign key hasn't a name.")
                    if fk['referred_table'] == table_name and \
                            column_name in fk['referred_columns']:
                        fk['source_table'] = ref_table_name
                        if 'options' not in fk:
                            fk['options'] = {}
                        fks.append(fk)

            ctx = MigrationContext.configure(conn)
            op = Operations(ctx)

            for fk in fks:
                op.drop_constraint(
                    fk['name'], fk['source_table'], type_="foreignkey")
            yield
            for fk in fks:
                op.create_foreign_key(
                    fk['name'], fk['source_table'],
                    fk['referred_table'],
                    fk['constrained_columns'],
                    fk['referred_columns'],
                    onupdate=fk['options'].get('onupdate'),
                    ondelete=fk['options'].get('ondelete'),
                    deferrable=fk['options'].get('deferrable'),
                    initially=fk['options'].get('initially'),
                )