package DBIx::DSN::Resolver;
use strict;
use warnings;
use DBI;
use Socket;
use Carp;
our $VERSION = '0.06';
sub new {
my $class = shift;
my %args = @_ == 1 ? %{$_[0]} : @_;
bless {
resolver => sub {
my $ipaddr = Socket::inet_aton($_[0]);
return unless $ipaddr;
Socket::inet_ntoa($ipaddr);
},
%args,
}, $class;
}
sub resolv {
my $self = shift;
my $dsn = shift;
return unless $dsn;
my ($scheme, $driver, $attr_string, $attr_hash, $driver_dsn)
= DBI->parse_dsn($dsn)
or croak "Can't parse DBI DSN '$dsn'";
my %driver_hash;
my @driver_hash_keys; #to keep order
for my $d ( split /;/, $driver_dsn ) {
my ( $k, $v) = split /=/, $d, 2;
$driver_hash{$k} = $v;
push @driver_hash_keys, $k;
}
my $host = $driver_hash{host};
return $dsn unless $host;
my $ipaddr = $self->{resolver}->($host)
or croak "Can't resolv host name: $host, $!";
$driver_hash{host} = $ipaddr;
$driver_dsn = join ';', map { $_.'='.$driver_hash{$_} } @driver_hash_keys;
$attr_string = defined $attr_string
? '('.$attr_string.')'
: '';
sprintf "%s:%s%s:%s", $scheme, $driver, $attr_string, $driver_dsn;
}
1;
__END__
=head1 NAME
DBIx::DSN::Resolver - Resolve hostname within dsn string
=head1 SYNOPSIS
use DBIx::DSN::Resolver;
my $dsn = 'dbi:mysql:database=mytbl;host=myserver.example'
my $resolver = DBIx::DSN::Resolver->new();
$dsn = $resolver->resolv($dsn);
is $dsn, 'dbi:mysql:database=mytbl;host=10.0.9.41';
=head1 DESCRIPTION
DBIx::DSN::Resolver parses dsn string and resolves hostname within dsn.
This module allows customize the resolver function.
=head1 CUSTOMIZE RESOLVER
use the resolver argument.
This sample code makes resolver cache with Cache::Memory::Simple.
use Cache::Memory::Simple;
use Socket;
my $DNS_CACHE = Cache::Memory::Simple->new(
size => 256,
);
my $r = DBIx::DSN::Resolver->new(
resolver => sub {
my $host = shift;
my $ipr = $DNS_CACHE->get($host);
my $ip = $ipr ? $$ipr : undef;
if ( !$ipr ) {
$ip = Socket::inet_aton($host);
$DNS_CACHE->set($host,\$ip,5);
}
return unless $ip;
Socket::inet_ntoa($ip);
}
);
$dsn = $resolver->resolv($dsn);
Default:
resolver => sub { Socket::inet_ntoa(Socket::inet_aton(@_)) }
=head1 AUTHOR
Masahiro Nagano E<lt>kazeburo {at} gmail.comE<gt>
=head1 NOTES
DBIx::DSN::Resolver uses Socket::inet_aton for hostname resolution.
If you use Solaris and fail hostname resolution, please recompile Socket with "LIBS=-lresolve"
=head1 LICENSE
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
=cut