File Coverage

blib/lib/CTK/DBI.pm
Criterion Covered Total %
statement 24 159 15.0
branch 1 68 1.4
condition 0 65 0.0
subroutine 8 26 30.7
pod 10 11 90.9
total 43 329 13.0


line stmt bran cond sub pod time code
1             package CTK::DBI; # $Id: DBI.pm 272 2019-09-26 08:45:46Z minus $
2 1     1   69447 use strict;
  1         12  
  1         31  
3 1     1   634 use utf8;
  1         15  
  1         5  
4              
5             =encoding utf-8
6              
7             =head1 NAME
8              
9             CTK::DBI - Database independent interface for CTKlib
10              
11             =head1 VERSION
12              
13             Version 2.30
14              
15             =head1 SYNOPSIS
16              
17             use CTK::DBI;
18              
19             # Enable debugging
20             # $CTK::DBI::CTK_DBI_DEBUG = 1;
21              
22             # MySQL connect
23             my $mso = new CTK::DBI(
24             -dsn => 'DBI:mysql:database=TEST;host=192.168.1.1',
25             -user => 'login',
26             -pass => 'password',
27             -connect_to => 5,
28             -request_to => 60
29             #-attr => {},
30             #-prepare_attr => {},
31             #-debug => 1,
32             );
33              
34             my $dbh = $mso->connect or die($mso->error);
35              
36             die($mso->error) if $mso->error;
37              
38             # Table select (as array)
39             my @result = $mso->table($sql, @inargs);
40              
41             # Table select (as hash)
42             my %result = $mso->tableh($key, $sql, @inargs); # $key - primary index field name
43              
44             # Record (as array)
45             my @result = $mso->record($sql, @inargs);
46              
47             # Record (as hash)
48             my %result = $mso->recordh($sql, @inargs);
49              
50             # Field (as scalar)
51             my $result = $mso->field($sql, @inargs);
52              
53             # SQL
54             my $sth = $mso->execute($sql, @inargs);
55             ...
56             $sth->finish;
57              
58             =head1 DESCRIPTION
59              
60             For example: print($mso->field("select sysdate() from dual"));
61              
62             =head2 new
63              
64             # MySQL connect
65             my $mso = new CTK::DBI(
66             -dsn => 'DBI:mysql:database=TEST;host=192.168.1.1',
67             -user => 'login',
68             -pass => 'password',
69             -connect_to => 5,
70             -request_to => 60
71             #-attr => {},
72             #-prepare_attr => {},
73             #-debug => 1,
74             );
75              
76             Create the DBI object
77              
78             =head2 error
79              
80             die $mso->error if $mso->error;
81              
82             Returns error string
83              
84             =head2 connect
85              
86             my $dbh = $mso->connect;
87              
88             Get DBH (DB handler)
89              
90             =head2 disconnect
91              
92             my $rc = $mso->disconnect;
93              
94             Forced disconnecting. Please not use this method
95              
96             =head2 execute
97              
98             # SQL
99             my $sth = $mso->execute($sql, @inargs);
100             ...
101             $sth->finish;
102              
103             Executing the SQL
104              
105             =head2 field
106              
107             # Fields (as scalar)
108             my $result = $mso->field($sql, @inargs);
109              
110             Get (select) field from database as scalar value
111              
112             =head2 record, recordh
113              
114             # Record (as array)
115             my @result = $mso->record($sql, @inargs);
116              
117             # Record (as hash)
118             my %result = $mso->recordh($sql, @inargs);
119              
120             Get (select) record from database as array or hash
121              
122             =head2 table, tableh
123              
124             # Table select (as array)
125             my @result = $mso->table($sql, @inargs);
126              
127             # Table select (as hash)
128             my %result = $mso->tableh($key, $sql, @inargs); # $key - primary index field name
129              
130             Get (select) table from database as array or hash
131              
132             =head1 HISTORY
133              
134             See C file
135              
136             =head1 VARIABLES
137              
138             =over 4
139              
140             =item B<$CTK::DBI::CTK_DBI_DEBUG>
141              
142             Debug mode flag. Default: 0
143              
144             =item B<$CTK::DBI::CTK_DBI_ERROR>
145              
146             General error string
147              
148             =back
149              
150             =head1 DEPENDENCIES
151              
152             L, L
153              
154             =head1 TO DO
155              
156             See C file
157              
158             =head1 BUGS
159              
160             * none noted
161              
162             =head1 SEE ALSO
163              
164             L, L
165              
166             =head1 AUTHOR
167              
168             Serż Minus (Sergey Lepenkov) L Eabalama@cpan.orgE
169              
170             =head1 COPYRIGHT
171              
172             Copyright (C) 1998-2019 D&D Corporation. All Rights Reserved
173              
174             =head1 LICENSE
175              
176             This program is free software; you can redistribute it and/or
177             modify it under the same terms as Perl itself.
178              
179             See C file and L
180              
181             =cut
182              
183 1     1   59 use Carp;
  1         2  
  1         57  
184 1     1   633 use CTK::Util qw( :API );
  1         3  
  1         236  
185              
186             use constant {
187 1 50       150 WIN => $^O =~ /mswin/i ? 1 : 0,
188             TIMEOUT_CONNECT => 60, # timeout of connect
189             TIMEOUT_REQUEST => 60, # timeout of request
190 1     1   8 };
  1         2  
191              
192             our $CTK_DBI_DEBUG = 0;
193             our $CTK_DBI_ERROR = "";
194 1     1   7 use vars qw/$VERSION/;
  1         3  
  1         119  
195             $VERSION = '2.30';
196              
197             my $LOAD_SigAction = 0;
198 1     1   677 eval 'use Sys::SigAction';
  1         5439  
  1         34  
199             my $es = $@;
200             if ($es) {
201             eval '
202             package # hide me from PAUSE
203             Sys::SigAction;
204             sub set_sig_handler($$;$$) { 1 };
205             1;
206             ';
207             _debug("Package Sys::SigAction don't installed! Please install this package") unless WIN;
208             } else {
209             $LOAD_SigAction = 1;
210             }
211              
212 1     1   3089 use DBI();
  1         18114  
  1         1675  
213              
214             sub new {
215 0     0 1   my $class = shift;
216 0           my @in = read_attributes([
217             ['DSN','STRING','STR'],
218             ['USER','USERNAME','LOGIN'],
219             ['PASSWORD','PASS'],
220             ['TIMEOUT_CONNECT','CONNECT_TIMEOUT','CNT_TIMEOUT','TIMEOUT_CNT','TO_CONNECT','CONNECT_TO'],
221             ['TIMEOUT_REQUEST','REQUEST_TIMEOUT','REQ_TIMEOUT','TIMEOUT_REQ','TO_REQUEST','REQUEST_TO'],
222             ['ATTRIBUTES','ATTR','ATTRS','ATTRHASH','PARAMS'],
223             ['PREPARE_ATTRIBUTES','PREPARE_ATTR','PREPARE_ATTRS'],
224             ['DEBUG'],
225             ], @_);
226 0 0         if ($in[7]) {
227 0           $CTK_DBI_DEBUG = 1;
228             }
229              
230             # General arguments
231 0   0       my %args = (
      0        
      0        
      0        
      0        
      0        
      0        
      0        
232             dsn => $in[0] || '',
233             user => $in[1] // '',
234             password => $in[2] // '',
235             connect_to => $in[3] || TIMEOUT_CONNECT,
236             request_to => $in[4] || TIMEOUT_REQUEST,
237             attr => $in[5] || undef,
238             prepare_attr=> $in[6] || undef,
239             debug => $in[7] // 0,
240             dbh => undef,
241             error => "", # Ok
242             );
243              
244             # Connect
245 0           my $_err = "";
246 0           $args{dbh} = DBI_CONNECT($args{dsn}, $args{user}, $args{password}, $args{attr}, $args{connect_to}, \$_err);
247 0           my $self = bless {%args}, $class;
248 0 0         if ($args{dbh}) { # Ok
249 0           _debug(sprintf("--- DBI CONNECT {%s} ---", $args{dsn}));
250             } else {
251 0           $self->_set_error($_err);
252             }
253              
254 0           return $self;
255             }
256              
257             sub _set_error {
258 0     0     my $self = shift;
259 0           my $merr = shift;
260 0           my $dbh = $self->{dbh};
261              
262 0 0 0       if (defined($merr)) {
    0          
    0          
263 0           $self->{error} = $merr;
264             } elsif ($dbh && $dbh->can('errstr')) {
265 0   0       $self->{error} = $self->{dbh}->errstr // '';
266             } elsif (defined($DBI::errstr)) {
267 0           $self->{error} = $DBI::errstr;
268             } else {
269 0           $self->{error} = "";
270             }
271 0 0 0       if ($dbh && $dbh->{PrintError}) {
272 0 0         carp(sprintf("%s: %s", __PACKAGE__, $self->{error})) if length($self->{error});
273             }
274              
275 0           return undef;
276             }
277              
278             sub error {
279 0     0 1   my $self = shift;
280 0   0       return $self->{error} // "";
281             }
282             sub connect {
283             # Returns dbh object
284 0     0 1   my $self = shift;
285 0           return $self->{dbh};
286             }
287             sub disconnect {
288 0     0 1   my $self = shift;
289 0 0         return unless $self->{dbh};
290 0           my $rc = $self->{dbh}->disconnect;
291 0   0       _debug(sprintf("--- DBI DISCONNECT {%s} ---", $self->{dsn} || ''));
292 0           return $rc;
293             }
294             sub field {
295 0     0 1   my $self = shift;
296 0           my @result = $self->record(@_);
297 0           return shift @result;
298             }
299             sub record {
300 0     0 1   my $self = shift;
301 0           my $sth = $self->execute(@_);
302 0 0         return () unless $sth;
303 0           my @result = $sth->fetchrow_array;
304 0           $sth->finish;
305 0           return @result;
306             }
307             sub recordh {
308 0     0 1   my $self = shift;
309 0           my $sth = $self->execute(@_);
310 0 0         return () unless $sth;
311 0           my $rslt = $sth->fetchrow_hashref;
312 0 0 0       $rslt = {} unless $rslt && ref($rslt) eq 'HASH';
313 0           my %result = %$rslt;
314 0           $sth->finish;
315 0           return %result;
316             }
317             sub table {
318 0     0 1   my $self = shift;
319 0           my $sth = $self->execute(@_);
320 0 0         return () unless $sth;
321 0           my $rslt = $sth->fetchall_arrayref;
322 0 0 0       $rslt = [] unless $rslt && ref($rslt) eq 'ARRAY';
323 0           my @result = @$rslt;
324 0           $sth->finish;
325 0           return @result;
326             }
327             sub tableh {
328 0     0 1   my $self = shift;
329 0           my $key_field = shift; # See keys (http://search.cpan.org/~timb/DBI-1.607/DBI.pm#fetchall_hashref)
330 0           my $sth = $self->execute(@_);
331 0 0         return () unless $sth;
332 0           my $rslt = $sth->fetchall_hashref($key_field);
333 0 0 0       $rslt = {} unless $rslt && ref($rslt) eq 'HASH';
334 0           my %result = %$rslt;
335 0           $sth->finish;
336 0           return %result;
337             }
338             sub execute {
339 0     0 1   my $self = shift;
340 0   0       my $sql = shift // '';
341 0           my @inargs = @_;
342 0   0       my $dbh = $self->{dbh} || return;
343 0 0         return $self->_set_error("No statement specified") unless length($sql);
344 0           $self->_set_error(""); # Flush errors
345              
346             # Prepare
347 0           my $prepare_attr = $self->{prepare_attr};
348 0 0 0       my %attr = ($prepare_attr && ref($prepare_attr) eq 'HASH') ? %$prepare_attr : ();
349 0 0         my $sth_ex = keys(%attr) ? $dbh->prepare($sql, {%attr}) : $dbh->prepare($sql);
350 0 0         unless ($sth_ex) {
351 0   0       return $self->_set_error(sprintf("Can't prepare statement \"%s\": %s", $sql, $dbh->errstr // 'unknown error'));
352             }
353              
354             # Execute
355 0           my $rto = $self->{request_to};
356 0           my $count_execute = 1; # TRUE
357 0           my $count_execute_msg = 'ok'; # TRUE
358 0           eval {
359 0 0   0     local $SIG{ALRM} = sub { die "execute timed out ($rto sec)" } unless $LOAD_SigAction;
  0            
360 0     0     my $h = Sys::SigAction::set_sig_handler( 'ALRM' ,sub { die "execute timed out ($rto sec)" ; } );
  0            
361 0           eval {
362 0           alarm($rto);
363 0 0         unless ($sth_ex->execute(@inargs)) {
364 0           $count_execute = 0; # FALSE
365 0           $count_execute_msg = $dbh->errstr; # FALSE
366             }
367 0           alarm(0);
368             };
369 0           alarm(0);
370 0 0         die $@ if $@;
371             };
372 0 0         if ( $@ ) {
373 0           $count_execute = 0; # FALSE
374 0           $count_execute_msg = $@;
375             }
376 0 0         unless ($count_execute) {
377 0           my @repsrgs = @inargs;
378 0           my $argb = "";
379 0 0         $argb = sprintf(" with bind variables: %s", join(", ", map {defined($_) ? sprintf("\"%s\"", $_) : 'undef'} @repsrgs))
  0 0          
380             if exists($inargs[0]);
381 0   0       return $self->_set_error(sprintf("Can't execute statement \"%s\"%s: %s", $sql, $argb || '', $count_execute_msg // 'unknown error'));
      0        
382             }
383              
384 0           return $sth_ex;
385             }
386              
387             sub DESTROY {
388 0     0     my $self = shift;
389 0           $self->disconnect();
390             }
391             sub DBI_CONNECT {
392             # Connect
393             # $dbh = DBI_CONNECT($dsn, $user, $password, $attr, $to, $error)
394             # IN:
395             # - DSN
396             # - DB Username
397             # - DB Password
398             # - Attributes DBD::* (hash-ref)
399             # - Timeout value
400             # <\ERROR> - Reference to error scalar
401             # OUT:
402             # $dbh - DataBase Handler Object
403             #
404 0   0 0 0   my $db_dsn = shift || ''; # DSN
405 0   0       my $db_user = shift // '';
406 0   0       my $db_password = shift // '';
407 0   0       my $db_attr = shift || {}; # E.g., {ORACLE_enable_utf8 => 1}
408 0   0       my $db_tocnt = shift || TIMEOUT_CONNECT;
409 0           my $rerr = shift;
410 0 0 0       $rerr = \$CTK_DBI_ERROR unless $rerr && ref($rerr) eq 'SCALAR';
411              
412 0           my $dbh;
413              
414 0           my $count_connect = 1; # TRUE
415 0           my $count_connect_msg = 'ok'; # TRUE
416 0           eval {
417 0 0   0     local $SIG{ALRM} = sub { die "connect timed out ($db_tocnt sec)" } unless $LOAD_SigAction;
  0            
418 0     0     my $h = Sys::SigAction::set_sig_handler( 'ALRM', sub { die "connect timed out ($db_tocnt sec)" } );
  0            
419 0           eval {
420 0           alarm($db_tocnt); #implement 2 second time out
421 0 0         unless ($dbh = DBI->connect($db_dsn, "$db_user", "$db_password", $db_attr)) {
422 0           $count_connect = 0; # FALSE
423 0           $count_connect_msg = $DBI::errstr;
424             }
425 0           alarm(0);
426             };
427 0           alarm(0);
428 0 0         die $@ if $@;
429             };
430 0 0         if ( $@ ) {
431             # Error
432 0           $count_connect = 0; # FALSE
433 0           $count_connect_msg = $@;
434             }
435 0 0         unless ($count_connect) {
436 0   0       $$rerr = sprintf("Can't connect to \"%s\", %s", $db_dsn, $count_connect_msg // 'unknown error');;
437             }
438              
439 0           return $dbh;
440             }
441              
442 0 0   0     sub _debug { $CTK_DBI_DEBUG ? carp(@_) : 1 }
443              
444             1;
445              
446             __END__