File Coverage

blib/lib/HealthCheck/Diagnostic/DBHCheck.pm
Criterion Covered Total %
statement 65 68 95.5
branch 27 30 90.0
condition 14 24 58.3
subroutine 11 11 100.0
pod 3 3 100.0
total 120 136 88.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   55599 use version;
  1         1620  
  1         5  
5             our $VERSION = 'v0.500.3'; # VERSION
6              
7 1     1   84 use 5.010;
  1         6  
8 1     1   5 use strict;
  1         2  
  1         15  
9 1     1   3 use warnings;
  1         2  
  1         22  
10 1     1   380 use parent 'HealthCheck::Diagnostic';
  1         227  
  1         5  
11              
12 1     1   1838 use Carp;
  1         2  
  1         44  
13 1     1   5 use Scalar::Util qw( blessed );
  1         2  
  1         635  
14              
15             sub new {
16 6     6 1 11485 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     29 ? %{ $params[0] } : @params;
  0         0  
21              
22             croak("The 'dbh' parameter should be a coderef!")
23 6 100 100     121 if ($params{dbh} && (ref $params{dbh} ne "CODE"));
24              
25 5         27 return $class->SUPER::new(
26             tags => [ 'dbh_check' ],
27             label => 'dbh_check',
28             %params
29             );
30             }
31              
32             sub check {
33 14     14 1 6769 my ( $self, %params ) = @_;
34              
35             # 1st, try to get dbh from provided parameters
36 14         25 my $dbh = $params{dbh};
37             # 2nd, if invoked with an object (not the class), then get dbh from object
38 14 100 66     47 $dbh ||= $self->{dbh} if ref $self;
39              
40 14 100       316 croak "Valid 'dbh' is required" unless $dbh;
41              
42 12 100       109 croak "The 'dbh' parameter should be a coderef!"
43             unless ref $dbh eq "CODE";
44              
45             my $db_access = $params{db_access} # Provided call to check()
46             // ((ref $self) && $self->{db_access}) # Value from new()
47 11   100     55 || "rw"; # default value
48              
49 11 100       116 croak "The value '$db_access' is not valid for the 'db_access' parameter"
50             unless $db_access =~ /^r[ow]$/;
51              
52 10         28 $dbh = $dbh->(%params);
53              
54 8 100       2664 croak "The 'dbh' coderef should return an object!"
55             unless blessed $dbh;
56              
57             my $db_class = $params{db_class} # Provided in call to check()
58             // ((ref $self) && $self->{db_class}) # Value from new
59 6   50     38 || "DBI::db"; # default value
60              
61 6         23 my $isa = ref $dbh;
62              
63 6 100       141 croak "The 'dbh' coderef should return a '$db_class', not a '$isa'"
64             unless $dbh->isa($db_class);
65              
66 5         22 my $res = $self->SUPER::check(
67             %params,
68             dbh => $dbh,
69             db_access => $db_access,
70             db_class => $db_class
71             );
72 5         295 delete $res->{dbh}; # don't include the object in the result
73              
74 5         18 return $res;
75             }
76              
77              
78             sub _read_write_temp_table {
79 2     2   7 my (%params) = @_;
80 2         6 my $dbh = $params{dbh};
81 2   50     7 my $table = $params{table_name} // "__DBH_CHECK__";
82 2         4 my $status = "CRITICAL";
83              
84 2         11 my $qtable = $dbh->quote_identifier($table);
85              
86             # Drop it like it's hot
87 2         1648 $dbh->do("DROP TEMPORARY TABLE IF EXISTS $qtable");
88              
89 2         68 $dbh->do(
90             join(
91             "",
92             "CREATE TEMPORARY TABLE IF NOT EXISTS $qtable (",
93             "check_id INTEGER PRIMARY KEY,",
94             "check_string VARCHAR(64) NOT NULL",
95             ")"
96             )
97             );
98              
99 2         489 $dbh->do(
100             join(
101             "",
102             "INSERT INTO $qtable ",
103             " (check_id, check_string) ",
104             "VALUES (1, 'Hello world')",
105             )
106             );
107 2         93 my @row = $dbh->selectrow_array(
108             "SELECT check_string FROM $qtable WHERE check_id = 1"
109             );
110              
111 2 50 33     143 $status = "OK" if ($row[0] && ($row[0] eq "Hello world"));
112              
113 2         12 $dbh->do("DROP TEMPORARY TABLE $qtable");
114              
115 2         44 return $status;
116             }
117              
118             sub run {
119 5     5 1 104 my ( $self, %params ) = @_;
120 5         11 my $dbh = $params{dbh};
121              
122             # Get db_access from parameters
123 5         17 my $read_write = ($params{db_access} =~ /^rw$/i);
124              
125 5         7 my $status = "OK";
126              
127             RUN_TESTS: {
128              
129             # See if we can ping the DB connection
130 5 100 66     7 if ($dbh->can("ping") && !$dbh->ping) {
  5         52  
131 2         23 $status = "CRITICAL";
132 2         5 last RUN_TESTS;
133             }
134              
135             # See if a simple SELECT works
136 3         47 my $value = eval { $dbh->selectrow_array("SELECT 1"); };
  3         18  
137 3 50 33     219 unless (defined $value && $value == 1) {
138 0         0 $status = "CRITICAL";
139 0         0 last RUN_TESTS;
140             }
141              
142 3 100       11 $status = _read_write_temp_table(%params) if $read_write;
143             }
144              
145             # Generate the human readable info string
146             my $info = sprintf(
147             "%s %s %s check of %s%s",
148             $status eq "OK" ? "Successful" : "Unsuccessful",
149             $dbh->{Driver}->{Name},
150             $read_write ? "read write" : "read only",
151             $dbh->{Name},
152 5 100       91 $dbh->{Username} ? " as $dbh->{Username}" : "",
    100          
    100          
153             );
154              
155 5         41 return { status => $status, info => $info };
156             }
157              
158             1;
159              
160             __END__