File Coverage

blib/lib/HealthCheck/Diagnostic/DBHCheck.pm
Criterion Covered Total %
statement 68 69 98.5
branch 32 36 88.8
condition 15 24 62.5
subroutine 11 11 100.0
pod 3 3 100.0
total 129 143 90.2


line stmt bran cond sub pod time code
1             package HealthCheck::Diagnostic::DBHCheck;
2              
3             # ABSTRACT: Check a database handle to make sure you have read/write access
4 1     1   70989 use version;
  1         2048  
  1         6  
5             our $VERSION = 'v0.500.1'; # VERSION
6              
7 1     1   129 use 5.010;
  1         7  
8 1     1   5 use strict;
  1         2  
  1         20  
9 1     1   5 use warnings;
  1         2  
  1         29  
10 1     1   456 use parent 'HealthCheck::Diagnostic';
  1         285  
  1         6  
11              
12 1     1   2304 use Carp;
  1         2  
  1         53  
13 1     1   6 use Scalar::Util qw( blessed );
  1         3  
  1         839  
14              
15             sub new {
16 6     6 1 14832 my ($class, @params) = @_;
17              
18             # Allow either a hashref or even-sized list of params
19             my %params = @params == 1 && ( ref $params[0] || '' ) eq 'HASH'
20 6 50 33     34 ? %{ $params[0] } : @params;
  0         0  
21              
22             croak("The 'dbh' parameter should be a coderef!")
23 6 100 100     151 if ($params{dbh} && (ref $params{dbh} ne "CODE"));
24              
25 5         29 return $class->SUPER::new(
26             label => 'dbh_check',
27             %params
28             );
29             }
30              
31             sub check {
32 13     13 1 8991 my ( $self, %params ) = @_;
33              
34             # 1st, try to get dbh from provided parameters
35 13         27 my $dbh = $params{dbh};
36             # 2nd, if invoked with an object (not the class), then get dbh from object
37 13 100 66     57 $dbh ||= $self->{dbh} if ref $self;
38              
39 13 100       398 croak("Valid 'dbh' is required") unless $dbh;
40              
41 11 100       134 croak("The 'dbh' parameter should be a coderef!")
42             unless (ref $dbh eq "CODE");
43              
44             my $db_access = $params{db_access} # Provided call to check()
45             // ((ref $self) && $self->{db_access}) # Value from new()
46 10   100     61 || "rw"; # default value
47              
48 10 100       139 croak("The value '$db_access' is not valid for the 'db_access' parameter")
49             unless ($db_access =~ /^r[ow]$/);
50              
51 9         18 eval{ local $SIG{__DIE__}; $dbh = $dbh->(%params); };
  9         34  
  9         35  
52              
53 9 100       3528 if($@) {
54 1         9 return { status => 'CRITICAL', info => $@ };
55             }
56              
57             return {
58 8 100       44 status => 'UNKNOWN',
59             info => "The 'dbh' coderef should return an object!"
60             } unless (blessed $dbh);
61              
62             my $db_class = $params{db_class} # Provided in call to check()
63             // ((ref $self) && $self->{db_class}) # Value from new
64 6   50     47 || "DBI::db"; # default value
65              
66 6         13 my $isa = ref $dbh;
67              
68             return {
69 6 100       45 status => 'UNKNOWN',
70             info => "The 'dbh' coderef should return a '$db_class', not a '$isa'"
71             } unless ($dbh->isa($db_class));
72              
73 5         27 my $res = $self->SUPER::check(
74             %params,
75             dbh => $dbh,
76             db_access => $db_access,
77             db_class => $db_class
78             );
79 5         333 delete $res->{dbh}; # don't include the object in the result
80              
81 5         21 return $res;
82             }
83              
84              
85             sub _read_write_temp_table {
86 2     2   9 my (%params) = @_;
87 2         5 my $dbh = $params{dbh};
88 2   50     30 my $table = $params{table_name} // "__DBH_CHECK__";
89 2         5 my $status = "CRITICAL";
90              
91 2         15 my $qtable = $dbh->quote_identifier($table);
92              
93             # Drop it like its hot
94 2         1837 $dbh->do("DROP TEMPORARY TABLE IF EXISTS $qtable");
95              
96 2         80 $dbh->do(
97             join(
98             "",
99             "CREATE TEMPORARY TABLE IF NOT EXISTS $qtable (",
100             "check_id INTEGER PRIMARY KEY,",
101             "check_string VARCHAR(64) NOT NULL",
102             ")"
103             )
104             );
105              
106 2         653 $dbh->do(
107             join(
108             "",
109             "INSERT INTO $qtable ",
110             " (check_id, check_string) ",
111             "VALUES (1, 'Hello world')",
112             )
113             );
114 2         123 my @row = $dbh->selectrow_array(
115             "SELECT check_string FROM $qtable WHERE check_id = 1"
116             );
117              
118 2 50 33     177 $status = "OK" if ($row[0] && ($row[0] eq "Hello world"));
119              
120 2         15 $dbh->do("DROP TEMPORARY TABLE $qtable");
121              
122 2         55 return $status;
123             }
124              
125             sub run {
126 5     5 1 149 my ( $self, %params ) = @_;
127 5         12 my $dbh = $params{dbh};
128              
129             # Get db_access from parameters
130 5         21 my $read_write = ($params{db_access} =~ /^rw$/i);
131              
132 5         10 my $status = "OK";
133              
134             # See if we can ping the DB connection
135 5 50       37 if ($dbh->can("ping")) {
136 5 100       24 $status = $dbh->ping ? "OK" : "CRITICAL";
137             }
138              
139 5 100       93 if ($status eq "OK") {
140             # See if a simple SELECT works
141 3         7 my $value = eval { $dbh->selectrow_array("SELECT 1"); };
  3         21  
142 3 50 33     253 $status = (defined($value) && ($value == 1)) ? "OK" : "CRITICAL";
143             }
144              
145 5 100 100     26 $status = _read_write_temp_table(%params)
146             if (($status eq "OK") && $read_write);
147              
148             # Generate the human readable info string
149             my $info = sprintf(
150             "%s %s %s check of %s%s",
151             $status eq "OK" ? "Successful" : "Unsuccessful",
152             $dbh->{Driver}->{Name},
153             $read_write ? "read write" : "read only",
154             $dbh->{Name},
155 5 100       119 $dbh->{Username} ? " as $dbh->{Username}" : "",
    100          
    100          
156             );
157              
158 5         51 return { status => $status, info => $info };
159             }
160              
161             1;
162              
163             __END__