File Coverage

blib/lib/Connector/Proxy/DBI.pm
Criterion Covered Total %
statement 98 118 83.0
branch 18 30 60.0
condition 11 18 61.1
subroutine 12 14 85.7
pod 6 6 100.0
total 145 186 77.9


line stmt bran cond sub pod time code
1             # Connector::Proxy::DBI
2             #
3             # Proxy class for fetching a value from using DBI
4             #
5             # Written by Oliver Welter and Martin Bartosch for the OpenXPKI project 2013
6              
7              
8             use strict;
9 1     1   170336 use warnings;
  1         15  
  1         26  
10 1     1   14 use English;
  1         2  
  1         24  
11 1     1   5 use DBI;
  1         7  
  1         31  
12 1     1   393 use Data::Dumper;
  1         3  
  1         38  
13 1     1   649  
  1         6142  
  1         60  
14             use Moose;
15 1     1   501 extends 'Connector::Proxy';
  1         394374  
  1         4  
16              
17              
18             has dbuser => (
19             is => 'rw',
20             isa => 'Str',
21             );
22              
23             has password => (
24             is => 'rw',
25             isa => 'Str',
26             );
27              
28             has table => (
29             is => 'rw',
30             isa => 'Str',
31             );
32              
33             has column => (
34             is => 'rw',
35             isa => 'Str|HashRef',
36             );
37              
38             has condition => (
39             is => 'rw',
40             isa => 'Str',
41             );
42              
43             has ambiguous => (
44             is => 'rw',
45             isa => 'Str',
46             default => 'empty',
47             );
48              
49             has _dbi => (
50             is => 'ro',
51             isa => 'Object',
52             lazy => 1,
53             builder => '_dbi_handle'
54             );
55              
56              
57              
58             my $self = shift;
59              
60 3     3   6 my $dsn = $self->LOCATION();
61              
62 3         89 my $dbh;
63             eval {
64 3         5 $dbh = DBI->connect($dsn, $self->dbuser(), $self->password(),
65 3         7 { RaiseError => 1, LongReadLen => 1024 });
66 3         93 };
67              
68             if ($EVAL_ERROR || !$dbh) {
69             $self->log()->error('DBI connect failed. DSN: '.$dsn. ' - Error: ' . $EVAL_ERROR );
70 3 50 33     1400 die "DBI connect failed."
71 0         0 }
72 0         0 return $dbh;
73              
74 3         115 }
75              
76              
77             my $self = shift;
78             my @path = $self->_build_path( shift );
79              
80 5     5 1 36 my $column = $self->column();
81 5         36 if (!$column || ref $column ne '') {
82             die "column must be a singe column name when using get";
83 5         190 }
84 5 50 33     35  
85 0         0 my $query = sprintf "SELECT %s FROM %s WHERE %s",
86             $column, $self->table(), $self->condition();
87              
88 5         216 $self->log()->debug('Query is ' . $query);
89              
90             my $sth = $self->_dbi()->prepare($query);
91 5         105 $sth->execute( @path );
92              
93 5         145 my $row = $sth->fetchrow_arrayref();
94 5         1122  
95             $self->log()->trace('result is ' . Dumper $row );
96 5         84  
97             my $result;
98 5         170 if (!$row) {
99             return $self->_node_not_exists( @path );
100 5         365  
101 5 50 100     166 } elsif (($self->ambiguous() ne 'return') && $sth->fetchrow_arrayref()) {
    100          
102 0         0  
103             $self->log()->error('Ambiguous (multi-valued) result');
104             if ($self->ambiguous() eq 'die') {
105             die "Ambiguous (multi-valued) result";
106 2         49 }
107 2 100       920 return $self->_node_not_exists( @path );
108 1         20  
109             }
110 1         9  
111             $self->log()->debug('Valid return: ' . $row->[0]);
112             return $row->[0];
113              
114 3         74 }
115 3         85  
116              
117             my $self = shift;
118             my @path = $self->_build_path( shift );
119              
120             my $column = $self->column();
121 6     6 1 49 if ($column && ref $column ne 'HASH') {
122 6         19 die "column must be a hashref or empty when using get_hash";
123             }
124 6         145  
125 6 50 66     23 my $columns = '*';
126 0         0 if (ref $column eq 'HASH') {
127             my @cols;
128             map {
129 6         15 push @cols, sprintf( "%s as %s", $column->{$_}, $_ );
130 6 100       13 } keys %{$column};
131 1         3 $columns = join(",", @cols);
132             }
133 3         11  
134 1         2 my $query = sprintf "SELECT %s FROM %s WHERE %s",
  1         4  
135 1         3 $columns, $self->table(), $self->condition();
136              
137             $self->log()->debug('Query is ' . $query);
138 6         123  
139             my $sth = $self->_dbi()->prepare($query);
140             $sth->execute( @path );
141 6         106  
142             my $row = $sth->fetchrow_hashref();
143 6         150  
144 6         710 $self->log()->trace('result is ' . Dumper $row );
145              
146 6         144 if (!$row) {
147             return $self->_node_not_exists( @path );
148 6         169  
149             } elsif (($self->ambiguous() ne 'return') && $sth->fetchrow_hashref()) {
150 6 100 100     476  
    100          
151 1         8 $self->log()->error('Ambiguous (multi-valued) result');
152             if ($self->ambiguous() eq 'die') {
153             die "Ambiguous (multi-valued) result";
154             }
155 1         27 return $self->_node_not_exists( @path );
156 1 50       339 }
157 1         12  
158             $self->log()->debug('Valid return: ' . Dumper $row);
159 0         0 return $row;
160              
161             }
162 4         76  
163 4         256  
164              
165             my $self = shift;
166             my @path = $self->_build_path( shift );
167              
168              
169             my $column = $self->column();
170 1     1 1 3 if (!$column || ref $column ne '') {
171 1         4 die "column must be a singe column name when using get_list";
172             }
173              
174 1         26 my $query = sprintf "SELECT %s FROM %s WHERE %s",
175 1 50 33     11 $self->column(), $self->table(), $self->condition();
176 0         0  
177             $self->log()->debug('Query is ' . $query);
178              
179 1         21 my $sth = $self->_dbi()->prepare($query);
180             $sth->execute( @path );
181              
182 1         18 my $rows = $sth->fetchall_arrayref();
183              
184 1         26 # hmpf
185 1         114 unless (ref $rows eq 'ARRAY') {
186             $self->log()->error('DBI did not return an arrayref');
187 1         39 die "DBI did not return an arrayref.";
188             }
189              
190 1 50       7 if (scalar @{$rows} == 0) {
191 0         0 return $self->_node_not_exists( @path );
192 0         0 }
193             my @result;
194             foreach my $row (@{$rows}) {
195 1 50       3 push @result, $row->[0];
  1         3  
196 0         0 }
197              
198 1         2 $self->log()->trace('result ' . Dumper \@result);
199 1         2  
  1         3  
200 1         4 $self->log()->debug('Valid return, '. scalar @result .' lines');
201             return @result;
202              
203 1         28 }
204              
205 1         70  
206 1         17 my $self = shift;
207             my @path = $self->_build_path( shift );
208              
209             my $query = sprintf "SELECT COUNT(*) as count FROM %s WHERE %s",
210             $self->table(), $self->condition();
211              
212 2     2 1 3 $self->log()->debug('Query is ' . $query);
213 2         7  
214             my $sth = $self->_dbi()->prepare($query);
215 2         57 $sth->execute( @path );
216              
217             my $row = $sth->fetchrow_arrayref();
218 2         38  
219             $self->log()->trace('Result is ' . Dumper $row);
220 2         52  
221 2         283 return $row->[0];
222              
223 2         33 }
224              
225 2         60  
226             my $self = shift;
227 2         124  
228             # If we have no path, we tell the caller that we are a connector
229             my @path = $self->_build_path( shift );
230             if (scalar @path == 0) {
231             return { TYPE => "connector" };
232             }
233 0     0 1    
234             # can be used as scalar also but list will be fine in any case
235             return { TYPE => 'list' };
236 0           }
237 0 0          
238 0            
239             # Will not catch get with an ambigous result :(
240              
241             my $self = shift;
242 0            
243             # No path = connector root which always exists
244             my @path = $self->_build_path( shift );
245             if (scalar @path == 0) {
246             return 1;
247             }
248              
249 0     0 1   return $self->get_size( \@path ) > 0;
250              
251             }
252 0            
253 0 0         no Moose;
254 0           __PACKAGE__->meta->make_immutable;
255              
256             1;
257 0            
258              
259             =head1 Name
260              
261 1     1   7507 Connector::Proxy::DBI
  1         2  
  1         4  
262              
263             =head1 Description
264              
265             Use DBI to make a query to a database.
266              
267             =head1 Usage
268              
269             =head2 Configuration
270              
271             my $con = Connector::Proxy::DBI->new({
272             LOCATION => 'DBI:mysql:database=openxpki;host=localhost',
273             dbuser => 'queryuser',
274             password => 'verysecret',
275             table => 'mytable',
276             column => 1,
277             condition => 'id = ?',
278             ambiguous => 'die',
279             });
280              
281             =head2 Parameters
282              
283             =over
284              
285             =item dbuser
286              
287             =item password
288              
289             =item table
290              
291             The name of the table, can also be a JOIN clause (if supported by the driver).
292              
293             =item column
294              
295             For get/get_list the name of a single column to be returned.
296              
297             For get_hash a hash where the keys are the target keys of the resulting
298             hash and the values are the column names.
299              
300             =item condition
301              
302             The condition using a question mark as placeholder. The placeholder(s) are
303             fed from the path components.
304              
305             =item ambigous
306              
307             Controls the behaviour of the connector if more than one result is found
308             when a single one is expected (get/get_hash).
309              
310             =over
311              
312             =item empty (default)
313              
314             Return an empty result, will also die if I<die_on_undef> is set.
315              
316             =item return
317              
318             The potential ambiguity is ignored and the first row fetched is returned.
319             Note that depending on the database backend the actual result returned from
320             the is very likely undetermined.
321              
322             =item die
323              
324             Die with an error message.
325              
326             =back
327              
328             =back
329              
330             =head1 Methods
331              
332             =head2 get
333              
334             Will return the value of the requested column of the matching row. If no row
335             is found, undef is returned (dies if die_on_undef is set).
336              
337             If multiple rows are found, behaviour depends on the value of I<ambiguous>.
338              
339             =head2 get_list
340              
341             Will return the selected column of all matching lines as a list. If no match is
342             found undef is returned (dies if die_on_undef is set).
343              
344             =head2 get_meta
345              
346             Will return scalar if the query has one result or list if the query has
347             multiple rows. Returns undef if no rows are found.
348              
349             =head2 get_hash
350              
351             Return a single row as hashref, by default all columns are returned as
352             retrieved from the database. Pass a hashref to I<column>, where the key
353             is the target key and the value is the name of the column you need.
354              
355             E.g. when your table has the columns id and name but you need the keys
356             index and title in your result.
357              
358             $con->column({ 'id' => 'id', '`index`' => 'id', 'title' => 'name' });
359              
360             Note: The mapping is set directly on the sql layer and as escaping
361             reserved words is not standardized, we dont do it. You can add escape
362             characters yourself to the column map where required, as shown for the
363             word "index" in the given example.
364              
365             =head2 get_keys
366              
367             not supported
368              
369