def test_add_rows(self):

        # I could also just create the database and add rows but I feel like doing this
        tables = self._get_tables()
        rows = {
            'logs': ({
                'id': 1,
                'message': 'hey',
                'traceback': 'never'
            }, {
                'id': 2,
                'message': 'sup',
                'traceback': 'forever'
            })
        }

        database = DatabaseReader(
            PyMySQL(connection=DbStructure(tables, rows)))
        database.read_rows('logs')

        operations = [str(op) for op in RowMygration(database).operations]
        self.assertEquals(2, len(operations))
        self.assertEquals(
            "INSERT INTO `logs` (`id`, `message`, `traceback`) VALUES ('1', 'hey', 'never');",
            operations[0])
        self.assertEquals(
            "INSERT INTO `logs` (`id`, `message`, `traceback`) VALUES ('2', 'sup', 'forever');",
            operations[1])
    def test_modify_rows(self):
        """ Types can change depending on whether or not rows come out of files or the database.  As a result, equality comparison has to ignore type differences """

        # I could also just create the database and add rows but I feel like doing this
        tables = self._get_tables()
        rows = {'logs': ({'id': 1, 'account_id': 1, 'message': 'hey'}, {'id': 2, 'account_id': 1, 'message': 'sup'})}

        database = DatabaseReader(PyMySQL(connection=DbStructure(tables, rows)))
        database.read_rows('logs')

        rows_from = {
            'logs': ({
                'id': 1,
                'account_id': '1',
                'message': 'hey'
            }, {
                'id': 2,
                'account_id': '1',
                'message': 'sup'
            })
        }
        database_from = DatabaseReader(PyMySQL(connection=DbStructure(tables, rows_from)))
        database_from.read_rows('logs')

        operations = [str(op) for op in RowMygration(database, database_from).operations]
        self.assertEquals(0, len(operations))
Beispiel #3
0
    def build_commands(self):

        files_database = DatabaseParser(self.get_sql_files())

        # any errors or warnings?
        quit_early = False
        if files_database.errors and not self.options.get('force'):
            print('Errors found in *.sql files', file=sys.stderr)
            quit_early = True
            for error in files_database.errors:
                print(error, file=sys.stderr)

        if quit_early:
            return []

        # use the credentials to load up a database connection
        live_database = DatabaseReader(self.get_driver())

        # we have to tell the live database to load records
        # for any tables we are tracking records for.
        # We use the "files" database as the "truth" because
        # just about any database can have records, but
        # that doesn't mean we want to track them.  We only
        # track them if the file has rows.
        for table in files_database.tables.values():
            if not table.tracking_rows:
                continue

            if not table.name in live_database.tables:
                continue

            live_database.read_rows(table)

        mygrate = Mygration(files_database, live_database, False)

        ops = []
        if mygrate.operations:
            ops.extend(mygrate.operations)

        rows = RowMygration(files_database, live_database)
        if rows.operations:
            ops.extend(rows.operations)

        if not ops:
            return []

        return [
            DisableChecks(),
            *ops,
            EnableChecks(),
        ]
Beispiel #4
0
    def execute(self, print_results=True):

        files_database = DatabaseParser(self.get_sql_files())

        # use the credentials to load up a database connection
        live_database = DatabaseReader(self.get_driver())

        # we aren't outputting operations.  Instead we just need to know what tables
        # have changed (either structure or records).  The easiest (and slightly hack)
        # way to do that is to simply run an actual mygration and grab out the names
        # of the tables that have changed.  Cheating, I know
        self.modified_tables = {}
        mygrate = Mygration(live_database, files_database, False)
        if mygrate.operations:
            for op in mygrate.operations:
                self.modified_tables[op.table_name] = True

        # we have to tell the live database to load records
        # for any tables we are tracking records for, according
        # to the files database.
        for table in files_database.tables.values():
            if not table.tracking_rows:
                continue
            if not table.name in live_database.tables:
                continue
            live_database.read_rows(table)

        rows = RowMygration(live_database, files_database)
        if rows.operations:
            for op in rows.operations:
                self.modified_tables[op.table_name] = True

        operations = []
        for table_name in self.modified_tables.keys():
            if print_results:
                print('\033[1;33m\033[41m%s\033[0m' % table_name)
            table = live_database.tables[table_name]
            operations.append(table.nice())
            if print_results:
                print(table.nice())

            if table.tracking_rows:
                if print_results:
                    print('\n')
                for row in table.rows.values():
                    operation = row_insert(table.name, row)
                    if print_results:
                        print(operation)
                    operations.append(operation)
        return (operations, True)
Beispiel #5
0
    def test_diffs_with_quotes(self):
        """ Things that need backslashes can cause trouble """

        # stick close to our use case: get the comparison table from the "database"
        tables = {
            'logs':
            """
                CREATE TABLE `logs` (
                    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
                    `message` TEXT NOT NULL,
                    `traceback` text,
                    PRIMARY KEY (`id`)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"""
        }

        rows = {
            'logs': ({
                'id': 1,
                'message': 'test\\sup',
                'traceback': 'never'
            }, {
                'id': 2,
                'message': 'sup\\test',
                'traceback': 'forever'
            })
        }

        db_db = DatabaseReader(PyMySQL(connection=DbStructure(tables, rows)))
        db_db.read_rows('logs')

        # and one from a file
        table1 = """CREATE TABLE `logs` (
            `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
            `message` TEXT NOT NULL,
            `traceback` text,
            PRIMARY KEY (`id`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

        INSERT INTO logs (id,message,traceback) VALUES (1,'test\\sup', 'never');
        INSERT INTO logs (id,message,traceback) VALUES (2,'bob\\test', 'forever');
        """
        files_db = FileReader([table1])

        mygrate = RowMygration(files_db, db_db)
        ops = [str(op) for op in mygrate]
        self.assertEquals(1, len(ops))
        self.assertEquals(
            "UPDATE `logs` SET `message`='bob\\\\test', `traceback`='forever' WHERE id='2';",
            ops[0])
    def test_all(self):

        # I could also just create the database and add rows but I feel like doing this
        tables = self._get_tables()
        rows = {
            'logs': ({
                'id': 2,
                'message': 'sup',
                'traceback': 'whatever'
            }, {
                'id': 3,
                'message': 'okay',
                'traceback': 'always'
            })
        }

        database = DatabaseReader(
            PyMySQL(connection=DbStructure(tables, rows)))
        database.read_rows('logs')

        rows_from = {
            'logs': ({
                'id': 1,
                'message': 'hey',
                'traceback': 'never'
            }, {
                'id': 2,
                'message': 'sup',
                'traceback': 'forever'
            })
        }
        database_from = DatabaseReader(
            PyMySQL(connection=DbStructure(tables, rows_from)))
        database_from.read_rows('logs')

        operations = [
            str(op) for op in RowMygration(database, database_from).operations
        ]
        # don't be picky about the order
        self.assertEquals(3, len(operations))
        self.assertTrue(
            "INSERT INTO `logs` (`id`, `message`, `traceback`) VALUES ('3', 'okay', 'always');"
            in operations)
        self.assertTrue("DELETE FROM `logs` WHERE id='1';" in operations)
        self.assertTrue(
            "UPDATE `logs` SET `message`='sup', `traceback`='whatever' WHERE id='2';"
            in operations)
    def test_modify_rows(self):

        # I could also just create the database and add rows but I feel like doing this
        tables = self._get_tables()
        rows = {
            'logs': ({
                'id': 1,
                'message': 'hey',
                'traceback': 'never'
            }, {
                'id': 2,
                'message': 'sup',
                'traceback': 'forever'
            })
        }

        database = DatabaseReader(
            PyMySQL(connection=DbStructure(tables, rows)))
        database.read_rows('logs')

        rows_from = {
            'logs': ({
                'id': 1,
                'message': 'hey',
                'traceback': 'bah'
            }, {
                'id': 2,
                'message': 'sup',
                'traceback': 'forever'
            })
        }
        database_from = DatabaseReader(
            PyMySQL(connection=DbStructure(tables, rows_from)))
        database_from.read_rows('logs')

        operations = [
            str(op) for op in RowMygration(database, database_from).operations
        ]
        self.assertEquals(1, len(operations))
        self.assertEquals(
            "UPDATE `logs` SET `message`='hey', `traceback`='never' WHERE id='1';",
            operations[0])
    def test_diff_with_null(self):
        """ NULL should be allowed and should result in a MySQL NULL in the database

        The system was turning NULL into a literal 'NULL'.  Internally, NULL
        is handled as a None
        """
        tables = {
            'logs':
            """
                CREATE TABLE `logs` (
                    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
                    `message` TEXT NOT NULL,
                    `traceback` text,
                    PRIMARY KEY (`id`)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"""
        }

        rows = {
            'logs': (
                {
                    'id': 1,
                    'message': 'from null to value',
                    'traceback': None
                },
                {
                    'id': 2,
                    'message': 'from value to null',
                    'traceback': 'forever'
                },
                {
                    'id': 3,
                    'message': 'from null to null',
                    'traceback': None
                },
            )
        }

        db_db = DatabaseReader(PyMySQL(connection=DbStructure(tables, rows)))
        db_db.read_rows('logs')

        # and one from a file
        table1 = """CREATE TABLE `logs` (
            `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
            `message` TEXT NOT NULL,
            `traceback` text,
            PRIMARY KEY (`id`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

        INSERT INTO logs (id,message,traceback) VALUES (1,'from null to value', 'HEY');
        INSERT INTO logs (id,message,traceback) VALUES (2,'from value to null', NULL);
        INSERT INTO logs (id,message,traceback) VALUES (3,'from null to null', NULL);
        INSERT INTO logs (id,message,traceback) VALUES (4,'Insert Null',NULL);
        """
        files_db = FileReader([table1])

        mygrate = RowMygration(files_db, db_db)
        ops = [str(op) for op in mygrate]
        self.assertEquals(3, len(ops))
        self.assertTrue(
            "INSERT INTO `logs` (`id`, `message`, `traceback`) VALUES ('4', 'Insert Null', NULL);"
            in ops)
        self.assertTrue(
            "UPDATE `logs` SET `message`='from null to value', `traceback`='HEY' WHERE id='1';"
            in ops)
        self.assertTrue(
            "UPDATE `logs` SET `message`='from value to null', `traceback`=NULL WHERE id='2';"
            in ops)