DBDesigner 4 の sqlt パーサを書いてみる(挫折編)

Rails だと DBDesigner 4 の吐く XML ファイルから model 等を生成するプロジェクトがあったりします*1が,PerlCatalyst 等)にはありません。キー,クヤシイ!

くやしがっていても仕方ないのでどう実現するか考えましょう。Perl だと様々なスキーマソースを相互変換する SQL-Translator プロジェクトがあるので,それ用のパーサ/プロデューサを書くと DBIC 等に持って行けるような気がします*2

パーサを書く方向性として

  • XSLT 等で SQL Fairy の XML 形式に変換して ...::Parser::XML::SQLFairy で読み込み
  • XML::XPath 等を用いて自力でパーサを書く

くらいしか思いつきませんが,XSLT についてはよくわからないし手間数が増えるのでパス。じゃあ自力かぁ…と思っていろいろ探していたら,FabForce::DBDesginer4 というモジュールを cpan で発見しました。おお,とりあえずこれを使って ...::Parser::XML::SQLFairy を真似っこしながら書けばいいんじゃね?

てことでいじってみましたが,だめだこの FabForce::DBDesigner4。

  • ファイルからしか読み込めない
  • データタイプが固定されたものと仮定して書いてある(<DATATYPE> タグを読んでない)からデータタイプを編集/新設してるとアウト
  • フィールド長を取得できない(致命的;DBIC にもってくだけなら充分かも)
  • インデックス定義,ビュー等を取得できない(致命的)

まぁ Wishlist として sqlt をサポートしたいという RT があがってるんで(作者本人ぽい),こちら方面はのんびり待ちますか。あーあ XML のパーサの勉強するかなぁ…

一応末尾にモジュールのせておきます。なんせファイルからしか読み込めないので,

    $ffdd->parsefile(xml => $translator->filename);

とか格好悪いことになってます。それゆえ標準入力等からは読めないであろう罠。

使い方は

sqlt -f FFDD4 -t YAML schema.xml 

とするとスキーマYAML で吐きますし,YAML の部分を MySQL にすると MySQL 用の SQL 文,PostgreSQL にすると PostgreSQL用の SQL 文になります。ってフィールド長がとれてないので意味がないのでした。

package SQL::Translator::Parser::FFDD4;

use strict;

use Exporter;
use base qw(Exporter);
our @EXPORT_OK = qw(parse);

use base qw(SQL::Translator::Parser);
use SQL::Translator::Utils qw(debug);

use FabForce::DBDesigner4;

our $DEBUG = 0;

sub parse {
    my ($translator, $data) = @_;
    my $schema   = $translator->schema;
    local $DEBUG = $translator->debug;
    my $ffdd     = FabForce::DBDesigner4->new();

    #$ffdd->parsefile(xml => $data);
    $ffdd->parsefile(xml => $translator->filename);

    foreach my $src_table ($ffdd->getTables()) {
        debug "Adding table:" . $src_table->name();

        my $table = $schema->add_table(name => $src_table->name())
            or die $schema->error;

        $table->primary_key($src_table->key());
        my %pkeys = map { $_ => 1 } $src_table->key();

        my @column_data = $src_table->stringsToTableCols($src_table->columns());
        foreach my $column_infos (@column_data) {
	    while (my ($field, $ref_infos) = each %$column_infos) {
                my ($data_type, $attrs) = @$ref_infos;
                my %fdata;

                $fdata{'name'}           = $field;
                $fdata{'data_type'}      = $data_type;
                #$fdata{'size'} IS MISSING!
                $fdata{'is_primary_key'} = $pkeys{$field} && 1;
                $fdata{'is_nullable'} 
                    = ($attrs !~ m' NOT \s+ NULL 'ixmso);
                $fdata{'is_auto_increment'}
                    = ($attrs =~ m' AUTOINCREMENT 'ixmso);

                my $field = $table->add_field(%fdata)
                    or die $table->error;
            }
        }
    }

    return 1;
}

1;

*1:dbmodel のページには「DBDesigner is now a legacy application」って書いてあって MySQL Workbench を使う方向に傾いているみたいですが

*2:直近のプロジェクトだと DBDesigner 4 で MySQL 用の SQL 文を吐かせて,そこから sqlt でさまざまな方面にもっていくというアプローチをとりました