File Coverage

blib/lib/App/JESP/Driver.pm
Criterion Covered Total %
statement 17 70 24.2
branch 1 26 3.8
condition 0 15 0.0
subroutine 5 10 50.0
pod 3 3 100.0
total 26 124 20.9


line stmt bran cond sub pod time code
1             package App::JESP::Driver;
2             $App::JESP::Driver::VERSION = '0.015';
3 2     2   4236 use Moose;
  2         5  
  2         16  
4              
5             =head1 NAME
6              
7             App::JESP::Driver - DB Specific stuff superclass.
8              
9             =cut
10              
11 2     2   8789 use Log::Any qw/$log/;
  2         7  
  2         20  
12 2     2   538 use DBI;
  2         4  
  2         97  
13 2     2   1335 use IPC::Run;
  2         44906  
  2         1623  
14              
15             has 'jesp' => ( is => 'ro' , isa => 'App::JESP', required => 1, weak_ref => 1);
16              
17             =head2 apply_patch
18              
19             Applies the given L<App::JESP::Patch> to the database. Dies in case of error.
20              
21             You do NOT need to implement that in subclasses.
22              
23             Usage:
24              
25             $this->apply_patch( $patch );
26              
27             =cut
28              
29             sub apply_patch{
30 5     5 1 13 my ($self, $patch) = @_;
31 5         144 $log->info("Applying patch ".$patch->id());
32 5 50       155 if( my $sql = $patch->sql() ){
33 5         31 $log->trace("Patch is SQL='$sql'");
34 5         30 return $self->apply_sql( $sql );
35             }
36 0 0         if( my $script_file = $patch->script_file() ){
37 0           $log->trace("Patch is SCRIPT='".$script_file."'");
38 0           return $self->apply_script( $script_file );
39             }
40             }
41              
42             =head2 apply_script
43              
44             Runs the given 'script' file, with the given environment:
45              
46             JESP_DSN : The full Perl DSN string
47             JESP_USER: DB User
48             JESP_PASSWORD: DB Password
49             JESP_SCHEME: 'dbi'
50             JESP_DRIVER: The name of the DBI driver in use
51             JESP_DRIVER_DSN: the part of the DSN after the driver
52              
53             Then the JESP_DRIVER_DSN is parsed and split into its components to generate environment variables.
54             The most common is:
55              
56             JESP_DATABASE: Name of the database to connect to
57             JESP_PORT: The port to connect to.
58             ...
59              
60             =cut
61              
62             sub apply_script{
63 0     0 1   my ($self, $script) = @_;
64              
65 0           my @cmd = ( $script );
66              
67 0           my $input = '';
68              
69             my $on_stdout = sub{
70 0     0     $log->info( @_ );
71 0           };
72 0           my @stderr;
73             my $on_stderr = sub{
74 0     0     $log->warn( @_ );
75 0           push @stderr , @_;
76 0           };
77              
78 0           my $properties = {};
79 0           my ($scheme, $driver, $attr_string, $attr_hash, $driver_dsn) = DBI->parse_dsn( $self->jesp()->dsn() );
80 0           ref($self)->_OdbcParse( $driver_dsn , $properties , [] );
81 0   0       $properties->{user} ||= $self->jesp()->username();
82 0   0       $properties->{password} ||= $self->jesp()->password();
83             $properties = {
84             %$properties,
85 0 0         %{ defined( $attr_hash ) ? $attr_hash : {} },
  0            
86             dsn => $self->jesp()->dsn(),
87             scheme => $scheme,
88             driver => $driver,
89             driver_dsn => $driver_dsn,
90             attr_string => $attr_string,
91             };
92 0           my %EXTRA_ENV = ();
93             # Outside testing, be verbose.
94 0 0 0       $EXTRA_ENV{IPCRUNDEBUG} = 'basic' unless( $ENV{AUTOMATED_TESTING} || $ENV{HARNESS_ACTIVE} );
95             # Transfer all the DB properties
96 0           foreach my $key ( keys %{$properties} ){
  0            
97 0 0         if( $properties->{$key} ){
98 0           $EXTRA_ENV{'JESP_'.uc($key)} = $properties->{$key};
99             }
100             }
101              
102 0           local %ENV = ( %ENV , %EXTRA_ENV );
103 0 0         IPC::Run::run( \@cmd , \$input , $on_stdout , $on_stderr ) or die join(' ', @cmd).": $? : ".join("\n", @stderr )."\n";
104             }
105              
106              
107             =head2 apply_sql
108              
109             Databases and their drivers vary a lot when it comes
110             to apply SQL patches. Some of them are just fine with sending
111             a blog of SQL to the driver, even when it contains multiple
112             statements and trigger or procedure, function definitions.
113              
114             Some of them require a specific implementation.
115              
116             This is the default implementation that just use the underlying DB
117             connection to send the patch SQL content.
118              
119             =cut
120              
121             sub apply_sql{
122 0     0 1   my ($self, $sql) = @_;
123 0           my $dbh = $self->jesp()->get_dbh()->();
124 0           my $ret = $dbh->do( $sql );
125 0 0         return defined($ret) ? $ret : confess( $dbh->errstr() );
126             }
127              
128              
129             # Shamelessly copied from DBD-mysql-4.043/lib/DBD/mysql.pm
130             sub _OdbcParse {
131 0     0     my($class, $dsn, $hash, $args) = @_;
132 0           my($var, $val);
133 0 0         if (!defined($dsn)) {
134 0           return;
135             }
136 0           while (length($dsn)) {
137 0 0         if ($dsn =~ /([^:;]*\[.*]|[^:;]*)[:;](.*)/) {
138 0           $val = $1;
139 0           $dsn = $2;
140 0           $val =~ s/\[|]//g; # Remove [] if present, the rest of the code prefers plain IPv6 addresses
141             } else {
142 0           $val = $dsn;
143 0           $dsn = '';
144             }
145 0 0         if ($val =~ /([^=]*)=(.*)/) {
146 0           $var = $1;
147 0           $val = $2;
148 0 0 0       if ($var eq 'hostname' || $var eq 'host') {
    0 0        
149 0           $hash->{'host'} = $val;
150             } elsif ($var eq 'db' || $var eq 'dbname') {
151 0           $hash->{'database'} = $val;
152             } else {
153 0           $hash->{$var} = $val;
154             }
155             } else {
156 0           foreach $var (@$args) {
157 0 0         if (!defined($hash->{$var})) {
158 0           $hash->{$var} = $val;
159 0           last;
160             }
161             }
162             }
163             }
164             }
165              
166             __PACKAGE__->meta()->make_immutable();