本番MySQLと開発MySQLのテーブル+カラムが一致しているか確認する(Perl,MySQL)

デプロイ時に、テーブルとかカラムを追加したり変更したりした時のtest用に。

use strict;
use warnings;

use Test::More;
BEGIN{ use_ok('DBI') }
BEGIN{ use_ok('Test::Fixture::DBI') }
BEGIN{ use_ok('Test::mysqld') }

my $db = 'myapp';

# 本番のDB
sub dbh {
    return DBI->connect( 'dbi:mysql:'. $db .':localhost','db_user','db_password', +{ 'AutoCommit' => 0, 'RaiseError' => 1 } );
}

# testのDB
sub test_dbh {
    my $mysqld = shift;
    return DBI->connect( $mysqld->dsn( dbname => 'test' ), 'root','', +{ AutoCommit => 0, RaiseError => 1, });
}

# testDBの作成
sub setup_test {
    my $mysqld = Test::mysqld->new( +{ my_cnf => +{ "skip-networking" => undef } });
    my $test_dbh = test_dbh( $mysqld );

    my $schema = construct_database(
        dbh      => $test_dbh,
        database => 'schema.yaml', # testDBで作成したschema(下記参照)
    );

    return $mysqld;
}

# 本番DB
my $dbh = dbh();

# testDB
my $mysqld = setup_test();
my $test_dbh = test_dbh($mysqld);

# testDBのテーブル一覧
my $test_tables = $test_dbh->prepare('SHOW TABLES');
$test_tables->execute;

for my $table ( @{ $test_tables->fetchall_arrayref(+{}) } ) {

    # 本番DBの該当テーブルのカラム
    my $column = $dbh->prepare('DESC '. $table->{'Tables_in_' . $db } );
    $column->execute;
    my $release = $column->fetchall_arrayref(+{});

    # testDBの該当テーブルのカラム
    my $test_column = $test_dbh->prepare('DESC '. $table->{'Tables_in_' . $db } );
    $test_column->execute;
    my $test = $test_column->fetchall_arrayref(+{});

    my $i = 0;
    for my $r ( @{ $release } ) {
        ok( $r->{'Field'} eq $test->[$i]->{'Field'} , $table->{'Tables_in_' . $db } . ' Field');

        if ( $r->{'Default'} ) {
            if ( $test->[$i]->{'Default'} ) {
                # 同じか確認
                ok( $r->{'Default'} eq $test->[$i]->{'Default'} , $table->{'Tables_in_' . $db } . ' Default(1)');
            }
            else {
                # testのDefaultが無い -> err
                ok( 0 , $table->{'Tables_in_' . $db } . ' Default(2)' );
            }
        }
        else {
            if ( $test->[$i]->{'Default'} ) {
                # testのDefaultがある -> err
                ok( 0 , $table->{'Tables_in_' . $db } . ' Default(3)' );
            }
        }

        ok( $r->{'Type'} eq $test->[$i]->{'Type'} , $table->{'Tables_in_' . $db } . ' Type');
        ok( $r->{'Null'} eq $test->[$i]->{'Null'} , $table->{'Tables_in_' . $db } . ' Null');
        ok( $r->{'Key'} eq $test->[$i]->{'Key'} , $table->{'Tables_in_' . $db } . ' Key');
        ok( $r->{'Extra'} eq $test->[$i]->{'Extra'} , $table->{'Tables_in_' . $db } . ' Extra');

        $i++;
    }
}

# 本番DB切断
$dbh->disconnect;

# testDB切断
$test_dbh->disconnect;

done_testing();

schema.yamlの生成(Test::Fixture::DBI)

Test::Fixture::DBIをインストールすると、make_database_yaml.plコマンドが使えるようになるので、下記のコマンドでschema.yamlを生成します。

make_database_yaml.pl -d "dbi:mysql:myapp:localhost" -u root -o schema.yaml

ま、手動でデプロイしない限りは本番と開発で違うとかないと思いますが・・。