Example #1
0
class Main(object):
    def __init__(self, config=None, mysql=None, db_migrate=None):
        self.__cli = CLI()
        self.__config = config

        self.__mysql = mysql
        if self.__mysql is None and not self.__config.get("new_migration"):
            self.__mysql = MySQL(config)

        self.__db_migrate = db_migrate
        if self.__db_migrate is None:
            self.__db_migrate = Migrations(config)

    def execute(self):
        self.__cli.msg("\nStarting DB migration...", "PINK")
        if self.__config.get("new_migration"):
            self._create_migration()
        else:
            self._migrate()
        self.__cli.msg("\nDone.\n", "PINK")

    def _create_migration(self):
        new_file = self.__db_migrate.create_migration(self.__config.get("new_migration"))
        self.__cli.msg("- Created file '%s'" % (new_file))

    def _migrate(self):
        destination_version = self._get_destination_version()
        current_version = self.__mysql.get_current_schema_version()

        self.__cli.msg("- Current version is: %s" % current_version, "GREEN")
        self.__cli.msg("- Destination version is: %s" % destination_version, "GREEN")

        # if current and destination versions are the same,
        # will consider a migration up to execute remaining files
        is_migration_up = True
        if int(current_version) > int(destination_version):
            is_migration_up = False

        # do it!
        self._execute_migrations(current_version, destination_version, is_migration_up)

    def _get_destination_version(self):
        destination_version = self.__config.get("schema_version")
        if destination_version is None:
            destination_version = self.__db_migrate.latest_schema_version_available()

        if not self.__db_migrate.check_if_version_exists(destination_version):
            self.__cli.error_and_exit("version not found (%s)" % destination_version)

        return destination_version

    def _get_migration_files_to_be_executed(self, current_version, destination_version):
        mysql_versions = self.__mysql.get_all_schema_versions()
        migration_versions = self.__db_migrate.get_all_migration_versions()

        # migration up: the easy part
        if current_version <= destination_version:
            remaining_versions_to_execute = Lists.subtract(migration_versions, mysql_versions)
            remaining_versions_to_execute = [
                version for version in remaining_versions_to_execute if version <= destination_version
            ]
            return remaining_versions_to_execute

        # migration down...
        down_versions = [
            version for version in mysql_versions if version <= current_version and version > destination_version
        ]
        for version in down_versions:
            if version not in migration_versions:
                self.__cli.error_and_exit(
                    "impossible to migrate down: one of the versions was not found (%s)" % version
                )
        down_versions.reverse()
        return down_versions

    def _execute_migrations(self, current_version, destination_version, is_migration_up):
        # getting only the migration sql files to be executed
        versions_to_be_executed = self._get_migration_files_to_be_executed(current_version, destination_version)

        if versions_to_be_executed is None or len(versions_to_be_executed) == 0:
            self.__cli.msg("\nNothing to do.\n", "PINK")
            sys.exit(0)

        up_down_label = "up" if is_migration_up else "down"
        if self.__config.get("show_sql_only"):
            self.__cli.msg("WARNING: database migrations are not being executed ('--showsqlonly' activated)", "YELLOW")
        else:
            self.__cli.msg("\nStarting migration %s!" % up_down_label)

        self.__cli.msg("*** versions: %s\n" % versions_to_be_executed, "GRAY")

        sql_statements_executed = []
        for migration_version in versions_to_be_executed:
            sql_file = self.__db_migrate.get_migration_file_name_from_version_number(migration_version)
            sql = self.__db_migrate.get_sql_command(sql_file, is_migration_up)

            if not self.__config.get("show_sql_only"):
                self.__cli.msg("===== executing %s (%s) =====" % (sql_file, up_down_label))
                self.__mysql.change(sql, migration_version, is_migration_up)

            # recording the last statement executed
            sql_statements_executed.append(sql)

        if self.__config.get("show_sql") or self.__config.get("show_sql_only"):
            self.__cli.msg("__________ SQL statements executed __________", "YELLOW")
            for sql in sql_statements_executed:
                self.__cli.msg(sql, "YELLOW")
            self.__cli.msg("_____________________________________________", "YELLOW")
Example #2
0
class Main(object):

    def __init__(self, config=None, mysql=None, db_migrate=None):
        self.cli = CLI()
        self.config = config or {}

        self.mysql = mysql
        if self.mysql is None and not self.config.get("new_migration"):
            self.mysql = MySQL(config)

        self.db_migrate = db_migrate or SimpleDBMigrate(config)

    def execute(self):
        self.cli.msg("\nStarting DB migration...", "PINK")
        if self.config.get("new_migration"):
            self.create_migration()
        else:
            self.migrate()
        self.cli.msg("\nDone.\n", "PINK")

    def create_migration(self):
        # TODO: create file in the migrations directory, not in current
        new_file = Migration.create(self.config.get("new_migration"))
        self.cli.msg("- Created file '%s'" % (new_file))

    def migrate(self):
        destination_version = self.get_destination_version()
        current_version = self.mysql.get_current_schema_version()

        self.cli.msg("- Current version is: %s" % current_version, "GREEN")
        self.cli.msg("- Destination version is: %s" % destination_version, "GREEN")

        # if current and destination versions are the same,
        # will consider a migration up to execute remaining files
        is_migration_up = True
        if int(current_version) > int(destination_version):
            is_migration_up = False

        # do it!
        self.execute_migrations(current_version, destination_version, is_migration_up)

    def get_destination_version(self):
        destination_version = self.config.get("schema_version")
        if destination_version is None:
            destination_version = self.db_migrate.latest_version_available()

        if destination_version is not '0' and not self.db_migrate.check_if_version_exists(destination_version):
            raise Exception("version not found (%s)" % destination_version)

        return destination_version

    def get_migration_files_to_be_executed(self, current_version, destination_version):
        mysql_versions = self.mysql.get_all_schema_versions()
        migration_versions = self.db_migrate.get_all_migration_versions()

        # migration up: the easy part
        if current_version <= destination_version:
            remaining_versions_to_execute = Lists.subtract(migration_versions, mysql_versions)
            remaining_versions_to_execute = [version for version in remaining_versions_to_execute if version <= destination_version]
            return remaining_versions_to_execute

        # migration down...
        down_versions = [version for version in mysql_versions if version <= current_version and version > destination_version]
        for version in down_versions:
            if version not in migration_versions:
                raise Exception("impossible to migrate down: one of the versions was not found (%s)" % version)
        down_versions.reverse()
        return down_versions

    def execute_migrations(self, current_version, destination_version, is_migration_up):
        # getting only the migration sql files to be executed
        versions_to_be_executed = self.get_migration_files_to_be_executed(current_version, destination_version)

        if versions_to_be_executed is None or len(versions_to_be_executed) == 0:
            self.cli.msg("\nNothing to do.\n", "PINK")
            return

        up_down_label = is_migration_up and "up" or "down"
        if self.config.get("show_sql_only"):
            self.cli.msg("\nWARNING: database migrations are not being executed ('--showsqlonly' activated)", "YELLOW")
        else:
            self.cli.msg("\nStarting migration %s!" % up_down_label)
        
        if self.config.get("log_level") >= 1:
            self.cli.msg("*** versions: %s\n" % versions_to_be_executed, "CYAN")

        sql_statements_executed = []
        for migration_version in versions_to_be_executed:
            migration = self.db_migrate.get_migration_from_version_number(migration_version)
            sql = is_migration_up and migration.sql_up or migration.sql_down

            if not self.config.get("show_sql_only"):
                if self.config.get("log_level") >= 1:
                    self.cli.msg("===== executing %s (%s) =====" % (migration.file_name, up_down_label))
                
                log = None
                if self.config.get("log_level") >= 2:
                    log = self.cli.msg
                
                self.mysql.change(sql, migration_version, is_migration_up, execution_log=log)
                
                # paused mode
                if self.config.get("paused_mode"):
                    raw_input("* press <enter> to continue... ")

            # recording the last statement executed
            sql_statements_executed.append(sql)

        if self.config.get("show_sql") or self.config.get("show_sql_only"):
            self.cli.msg("__________ SQL statements executed __________", "YELLOW")
            for sql in sql_statements_executed:
                self.cli.msg(sql, "YELLOW")
            self.cli.msg("_____________________________________________", "YELLOW")
Example #3
0
class Main(object):

    def __init__(self, config=None, mysql=None, db_migrate=None):
        self.cli = CLI()
        self.config = config or {}
        self.log = LOG(self.config.get("log_dir"))

        self.mysql = mysql
        if self.mysql is None and not self.config.get("new_migration"):
            self.mysql = MySQL(config)

        self.db_migrate = db_migrate or SimpleDBMigrate(config)

    def execution_log(self, msg, color="CYAN", log_level_limit=2):
        if self.config.get("log_level") >= log_level_limit:
            self.cli.msg(msg)
        self.log.debug(msg)

    def execute(self):
        self.execution_log("\nStarting DB migration...", "PINK", log_level_limit=1)
        if self.config.get("new_migration"):
            self.create_migration()
        else:
            self.migrate()
        self.execution_log("\nDone.\n", "PINK", log_level_limit=1)

    def create_migration(self):
        migrations_dir = self.config.get("migrations_dir")
        new_file = Migration.create(self.config.get("new_migration"), migrations_dir[0], self.config.get("db_script_encoding", "utf-8"))
        self.execution_log("- Created file '%s'" % (new_file), log_level_limit=1)

    def migrate(self):
        destination_version = self.get_destination_version()
        current_version = self.mysql.get_current_schema_version()

        # do it!
        self.execute_migrations(current_version, destination_version)

    def get_destination_version(self):
        destination_version = self.config.get("schema_version")
        if destination_version is None:
            destination_version = self.db_migrate.latest_version_available()

        if destination_version is not '0' and not self.db_migrate.check_if_version_exists(destination_version):
            raise Exception("version not found (%s)" % destination_version)

        return destination_version

    def get_migration_files_to_be_executed(self, current_version, destination_version, is_migration_up):
        mysql_versions = self.mysql.get_all_schema_versions()
        migration_versions = self.db_migrate.get_all_migration_versions()

        # migration up
        if is_migration_up:
            remaining_versions_to_execute = Lists.subtract(migration_versions, mysql_versions)
            remaining_migrations_to_execute = [self.db_migrate.get_migration_from_version_number(version) for version in remaining_versions_to_execute if version <= destination_version]
            return remaining_migrations_to_execute

        # migration down...
        destination_version_id = self.mysql.get_version_id_from_version_number(destination_version)
        migrations = self.mysql.get_all_schema_migrations()
        down_migrations_to_execute = [migration for migration in migrations if migration.id > destination_version_id]
        for migration in down_migrations_to_execute:
            if not migration.sql_down:
                if migration.version not in migration_versions:
                    raise Exception("impossible to migrate down: one of the versions was not found (%s)" % migration.version)
                migration_tmp = self.db_migrate.get_migration_from_version_number(migration.version)
                migration.sql_up = migration_tmp.sql_up
                migration.sql_down = migration_tmp.sql_down
                migration.file_name = migration_tmp.file_name

        down_migrations_to_execute.reverse()
        return down_migrations_to_execute

    def execute_migrations(self, current_version, destination_version):
        """
        passed a version:
            this version don't exists in the database and is younger than the last version -> do migrations up until the this version
            this version don't exists in the database and is older than the last version -> do nothing, is a unpredictable behavior
            this version exists in the database and is older than the last version -> do migrations down until this version

        didn't pass a version -> do migrations up until the last available version
        """

        is_migration_up = True
        # check if a version was passed to the program
        if self.config.get("schema_version"):
            # if was passed and this version is present in the database, check if is older than the current version
            destination_version_id = self.mysql.get_version_id_from_version_number(destination_version)
            if destination_version_id:
                current_version_id = self.mysql.get_version_id_from_version_number(current_version)
                # if this version is previous to the current version in database, then will be done a migration down to this version
                if current_version_id > destination_version_id:
                    is_migration_up = False
            # if was passed and this version is not present in the database and is older than the current version, raise an exception
            # cause is trying to go down to something that never was done
            elif current_version > destination_version:
                raise Exception("Trying to migrate to a lower version wich is not found on database (%s)" % destination_version)

        # getting only the migration sql files to be executed
        migrations_to_be_executed = self.get_migration_files_to_be_executed(current_version, destination_version, is_migration_up)

        self.execution_log("- Current version is: %s" % current_version, "GREEN", log_level_limit=1)

        if migrations_to_be_executed is None or len(migrations_to_be_executed) == 0:
            self.execution_log("- Destination version is: %s" % current_version, "GREEN", log_level_limit=1)
            self.execution_log("\nNothing to do.\n", "PINK", log_level_limit=1)
            return

        self.execution_log("- Destination version is: %s" % (is_migration_up and migrations_to_be_executed[-1].version or destination_version), "GREEN", log_level_limit=1)

        up_down_label = is_migration_up and "up" or "down"
        if self.config.get("show_sql_only"):
            self.execution_log("\nWARNING: database migrations are not being executed ('--showsqlonly' activated)", "YELLOW", log_level_limit=1)
        else:
            self.execution_log("\nStarting migration %s!" % up_down_label, log_level_limit=1)

        if self.config.get("log_level") >= 1:
            self.execution_log("*** versions: %s\n" % ([ migration.version for migration in migrations_to_be_executed]), "CYAN", log_level_limit=1)

        sql_statements_executed = []
        for migration in migrations_to_be_executed:
            sql = is_migration_up and migration.sql_up or migration.sql_down

            if not self.config.get("show_sql_only"):
                if self.config.get("log_level") >= 1:
                    self.execution_log("===== executing %s (%s) =====" % (migration.file_name, up_down_label), log_level_limit=1)

                self.mysql.change(sql, migration.version, migration.file_name, migration.sql_up, migration.sql_down, is_migration_up, execution_log=self.execution_log)

                # paused mode
                if self.config.get("paused_mode"):
                    raw_input("* press <enter> to continue... ")

            # recording the last statement executed
            sql_statements_executed.append(sql)

        if self.config.get("show_sql") or self.config.get("show_sql_only"):
            self.execution_log("__________ SQL statements executed __________", "YELLOW", log_level_limit=1)
            for sql in sql_statements_executed:
                self.execution_log(sql, "YELLOW", log_level_limit=1)
            self.execution_log("_____________________________________________", "YELLOW", log_level_limit=1)