icdbi - Class::DBIを使った対話型のSQLクライアント


だいぶ前に作って放置してたやつ。

id:secondlife さんの iar - ActiveRecordを使った対話型のSQLクライアント - 2nd lifeを見てClass::DBIでも作って(パクって)みた。

Class::DBIだと、標準でActiveRecord みたいに便利じゃないので、あんまり意味ないかも。

ソースは以下。

#!/usr/local/bin/perl
use strict;
use warnings;

use Data::Dumper;
use Term::ReadLine;
use Getopt::Long;
use Class::DBI;
use Class::DBI::Loader;

my $USAGE = <<'END_USAGE';
usage: icdbi [-a adapter] [-h host] [-P port] [-u user] [-p password] database
END_USAGE

local $Data::Dumper::Terse  = 1;
local $Data::Dumper::Indent = 1;

Getopt::Long::Configure qw(bundling no_ignore_case);
my $opt = {};
GetOptions($opt,
    'h|host=s',
    'D|database=s',
    'u|user=s',
    'p|password=s',
    'P|port=s',
    'a|adapter=s',
    'help!',
);
print $USAGE and exit if exists $opt->{help};
my ($host, $database, $user, $password, $port, $adapter)
    = map { $opt->{$_} || '' } qw(h D u p P a);
$database ||= pop or die $USAGE;
$user     ||= 'root';
$password ||= '';
$adapter  ||= 'mysql';

my $dsn = "dbi:$adapter:$database";
$dsn .= ";host=$host" if $host;
$dsn .= ";port=$port" if $port;

my $loader = Class::DBI::Loader->new(
    dsn           => $dsn,
    user          => $user,
    password      => $password,
    options       => { RaiseError => 1, AutoCommit => 1 },
    relationships => 1,
);
for ($loader->classes) {
    $_->columns(Essential => $_->columns('All'));
    print "$_ loaded.\n";
}
print "\n";

my $term = Term::ReadLine->new('icdbi');
my $prompt = 'icdbi[%d]$ ';
my $line = 0;
my $code = '';

while (defined(my $input = $term->readline($code ? '> ' : sprintf($prompt, $line)))) {
    next unless $input;
    quit() if $input eq 'quit' or $input =~ /\\q$/;
    if ($input =~ /\\c$/) {
        clear();
        next;
    }
    $line++;
    $code .= $input;
    next unless $code =~ /;$/;
    execute();
    clear();
}

sub execute {
    my @ret;
    {
        package ICDBI;
        no strict;
        no warnings;
        @ret = eval $code;
    }
    print Dumper @ret;
    warn $@ if $@;
    print "\n";
}

sub clear {
    $code = '';
}

sub quit {
    print "Bye\n" and exit;
}

__END__

Class::DBI::Loader が必要。
あと、Term::ReadLine::Gnuを入れておくと操作が快適。

DBIx::Classな人は

my $loader = DBIx::Class::Loader->new(
    dsn           => $dsn,
    user          => $user,
    password      => $password,
    options       => { RaiseError => 1, AutoCommit => 1 },
    relationships => 1,
    namespace     => 'Data',
);
for ($loader->classes) {
    print "$_ loaded.\n";
}

にすれば使えるはず。

使い方は、上のコードをicdbi とかの名前でPATHの通ったところにおいて

icdbi -u user -p password hoge_db

とすると、こんな感じに

icdbi[1]> $hoge = Hoge->retrieve(1);
icdbi[2]> $hoge->body('hogehoge');
idbic[3]> $hoge->update;

とかできる。


-a SQLite とかするとSQLiteでも使える。

Class::DBI::Loaderが対応してるやつなら使えるはず。


実行しているスコープが微妙なのと、コードがいまいちなのが問題・・・


#最近のClass::DBI::Loader が、Class::DBI を use しないと動かないっぽい。いつからだろ。