File Coverage

blib/lib/DBIx/Connection.pm
Criterion Covered Total %
statement 30 293 10.2
branch 0 96 0.0
condition 0 62 0.0
subroutine 10 56 17.8
pod 40 40 100.0
total 80 547 14.6


line stmt bran cond sub pod time code
1             package DBIx::Connection;
2              
3 6     6   196122 use warnings;
  6         14  
  6         206  
4 6     6   33 use strict;
  6         9  
  6         207  
5 6     6   22321 use DBI;
  6         115338  
  6         426  
6 6     6   6117 use DBI::Const::GetInfoType;
  6         56541  
  6         944  
7 6     6   6113 use Abstract::Meta::Class ':all';
  6         89405  
  6         1292  
8 6     6   4403 use DBIx::SQLHandler;
  6         15  
  6         172  
9 6     6   3474 use DBIx::QueryCursor;
  6         16  
  6         164  
10 6     6   41 use Carp 'confess';
  6         12  
  6         307  
11 6     6   60 use vars qw($VERSION $CONNECTION_POOLING $IDLE_THRESHOLD);
  6         19  
  6         324  
12 6     6   6866 use Time::HiRes qw(gettimeofday tv_interval);
  6         12700  
  6         31  
13              
14             $VERSION = 0.08;
15             $IDLE_THRESHOLD = 300;
16              
17             =head1 NAME
18              
19             DBIx::Connection - Simple database interface.
20              
21             =head1 SYNOPSIS
22              
23             use DBIx::Connection;
24             my $connection = DBIx::Connection->new(
25             name => 'my_connection_name',
26             dsn => 'dbi:Oracle:localhost:1521/ORCL',
27             username => 'user',
28             password => 'password',
29             db_session_variables => {
30             NLS_DATE_FORMAT => 'DD.MM.YYYY'
31             }
32             );
33              
34             or
35             my $dbh = DBI->connect(...);
36             my $connection = DBIx::Connection->new(
37             name => 'my_connection_name',
38             dbh => $dbh,
39             db_session_variables => {
40             NLS_DATE_FORMAT => 'DD.MM.YYYY'
41             }
42             );
43              
44              
45             my $cursor = $connection->query_cursor(sql => "select * from emp where deptno > ?", name => 'emp_select');
46             my $dataset = $cursor->execute(20);
47             while ($cursor->fetch) {
48             #do some stuff ...
49             print $_ . " => " . $dataset->{$_}
50             for keys %$dataset;
51             }
52              
53             {
54             my $cursor = $connection->find_query_cursor('emp_select');
55             my $dataset = $cursor->execute(20);
56             ...
57             }
58              
59              
60             my $record = $connection->record("select * from emp where empno = ?", 'xxx');
61              
62             my $sql_handler = $connection->sql_handler(sql => "INSERT INTO emp(empno, ename) VALUES(?, ?)", name => 'emp_ins');
63             $sql_handler->execute(1, 'Smith');
64             $sql_handler->execute(2, 'Witek');
65              
66             {
67             my $sql_handler= $connection->find_sql_handler('emp_ins');
68             $sql_handler->execute(3, 'Zzz');
69             ...
70             }
71              
72             #or
73              
74             $connection->execute_statement("INSERT INTO emp(empno, ename) VALUES(?, ?)", 1, 'Smith');
75              
76              
77              
78             #gets connection by name.
79             my $connection = DBIx::Connection->connection('my_connection_name');
80              
81             do stuff
82              
83             # returns connection to connection pool
84             $connection->close();
85              
86              
87             #turn on connection pooling
88             $DBIx::Connection::CONNECTION_POOLING = 1;
89              
90             In this mode only connection may have the following states : in_use and NOT in_use,
91             Only connection that is "NOT in use" state can be retrieve by invoking DBIx::Connection->connection, and
92             state changes to "in use". Close method change state back to NOT in_use.
93             If in connection pool there are not connections in "NOT in use" state, then the new connection is cloned.
94              
95             my $connection = DBIx::Connection->connection('my_connection_name');
96              
97              
98             # do stuff ...
99             $connection->close();
100              
101             #preserving resource by physical disconnecting all connection that are idle by defined threshold (sec).
102             $DBIx::Connection::IDLE_THRESHOLD = 300;
103              
104              
105             =head1 DESCRIPTION
106              
107             Represents a database connection handler.
108              
109             It provides simple interface to managing database connections with the all related operations wrapped in the
110             different sql handlers.
111              
112             $connection = DBIx::Connection->connection('my_connection_name');
113              
114             eval {
115             $connection->begin_work();
116             my $sql_handler = $connection->sql_handler(sql => "INSERT INTO emp(empno, ename) VALUES(?, ?)");
117             $sql_handler->execute(1, 'Smith');
118             ...
119              
120             $connection->commit();
121             };
122              
123             if($@) {
124             $connection->rollback();
125             }
126              
127             $connection->close();
128              
129             It supports:
130              
131             sql handlers(dml) -(INSERT/UDPDATE/DELETE)
132              
133             my $sql_handler = $connection->sql_handler(sql => "INSERT INTO emp(empno, ename) VALUES(?, ?)");
134             $sql_handler->execute(1, 'Smith');
135              
136             query cursors - SELECT ... FROM ...
137              
138             my $query_cursor = $connection->query_cursor(
139             sql => "
140             SELECT t.* FROM (
141             SELECT 1 AS col1, 'text 1' AS col2 " . ($dialect eq 'oracle' ? ' FROM dual' : '') . "
142             UNION ALL
143             SELECT 2 AS col1, 'text 2' AS col2 " . ($dialect eq 'oracle' ? ' FROM dual' : '') . "
144             ) t
145             WHERE 1 = ? "
146             );
147             my $resultset = $cursor->execute([1]);
148             while($cursor->fetch()) {
149             # do some stuff
150             # $resultset
151             }
152              
153             plsql handlers - BEGIN ... END
154              
155             my $plsql_handler = $connection->plsql_handler(
156             name => 'test_block',
157             connection => $connection,
158             plsql => "BEGIN
159             :var1 := :var2 + :var3;
160             END;",
161             bind_variables => {
162             var1 => {type => 'SQL_INTEGER'},
163             var2 => {type => 'SQL_INTEGER'},
164             var3 => {type => 'SQL_INTEGER'}
165             }
166             );
167             my $resultset = $plsql_handler->execute(var2 => 12, var3 => 8);
168              
169              
170             Connection is cached by its name.
171              
172             DBIx::Connection->new(
173             name => 'my_connection_name',
174             dsn => 'dbi:Oracle:localhost:1521/ORCL',
175             username => 'user',
176             password => 'password',
177             );
178              
179             $connection = DBIx::Connection->connection('my_connection_name');
180              
181             RDBMS session variables supports.
182              
183             my $databaseHandler = DBIx::Connection->new(
184             name => 'my_connection_name',
185             dsn => 'dbi:Oracle:localhost:1521/ORCL',
186             username => 'user',
187             password => 'password',
188             db_session_variables => {
189             NLS_DATE_FORMAT => 'DD.MM.YYYY'
190             }
191             )
192              
193             It caches sql statements based on handler's name.
194              
195             $connection->sql_handler(name => 'emp_ins', sql => "INSERT INTO emp(empno, ename) VALUES(?, ?)");
196             my $sql_handler = $connection->find_sql_handler('emp_ins');
197             $sql_handler->execute(1, 'Smith');
198              
199              
200             Database usage:
201              
202             This module allows gathering sql statistics issued by application
203              
204             Automatic reporting:
205              
206             $connection->set_collect_statistics(1);
207             $connection->set_statistics_dir('/sql_usage');
208              
209              
210             Error handler customization:
211              
212             It supports eroror handler customization.
213              
214             my $error_handler = sub {
215             my (self, $message, $sql_handler) = @_;
216             #do some stuff
217             };
218             $connection->set_custom_error_handler($error_handler);
219              
220              
221             Sequences support:
222              
223             $connection->sequence_value('emp_seq');
224              
225             Large Object support;
226              
227             $connection->update_lob(lob_test => 'blob_content', $lob_content, {id => 1}, 'doc_size');
228             my $lob = $connection->fetch_lob(lob_test => 'blob_content', {id => 1}, 'doc_size');
229              
230              
231             =head2 ATTRIBUTES
232              
233             =over
234              
235             =item name
236              
237             Connection name.
238              
239             =cut
240              
241             has '$.name';
242              
243              
244             =item dsn
245              
246             Database source name.
247              
248             =cut
249              
250             has '$.dsn';
251              
252              
253             =item username
254              
255             =cut
256              
257             has '$.username';
258              
259              
260             =item password
261              
262             =cut
263              
264             has '$.password';
265              
266              
267             =item database handler
268              
269             =cut
270              
271             has '$.dbh';
272              
273              
274             =item db_session_variables
275              
276             =cut
277              
278             has '%.db_session_variables';
279              
280              
281             =item query_cursors
282              
283             =cut
284              
285             has '%.query_cursors' => (item_accessor => '_query_cursor');
286              
287              
288             =item sql_handlers
289              
290             =cut
291              
292             has '%.sql_handlers' => (item_accessor => '_sql_handler');
293              
294              
295              
296             =item plsql_handlers
297              
298             =cut
299              
300             has '%.plsql_handlers' => (item_accessor => '_plsql_handler');
301              
302              
303             =item custom_error_handler
304              
305             Callback that overwrites default error_handler on SQLHandler object.
306              
307             =cut
308              
309             has '&.custom_error_handler';
310              
311              
312             =item stats
313              
314             =cut
315              
316             has '%.tracking';
317              
318              
319             =item action_start_time
320              
321             =cut
322              
323             has '$.action_start_time';
324              
325              
326             =item collect_statistics
327              
328             Flag that indicate if statisitcs are collected.
329              
330             =cut
331              
332             has '$.collect_statistics' => (default => 0);
333              
334              
335             =item statistics_dir
336              
337             =cut
338              
339             has '$.statistics_dir';
340              
341              
342             =item in_use
343              
344             =cut
345              
346             has '$.in_use';
347              
348              
349              
350             =item is_connected
351              
352             =cut
353              
354             has '$.is_connected';
355              
356              
357             =item last_in_use
358              
359             =cut
360              
361             has '$.last_in_use';
362              
363              
364              
365             =item no_cache
366              
367             Prepares statements each time, otherwise use prepare statement once and reuse it
368              
369             =cut
370              
371             has '$.no_cache';
372              
373              
374             =item _active_transaction
375              
376             Flag that indicate that connection has pending transaction
377              
378             =cut
379              
380             has '$._active_transaction';
381              
382              
383             =back
384              
385             =head2 METHODS
386              
387             =over
388              
389             =item load_module
390              
391             Loads specyfic rdbms module.
392              
393             =cut
394              
395             {
396             my %loaded_modules = ();
397             sub load_module {
398 0     0 1   my ($self, $module) = @_;
399 0           my $rdbms_module = $self->dbms_name . "::" . $module;
400 0 0         return $loaded_modules{$rdbms_module} if $loaded_modules{$rdbms_module};
401 0           my $module_name = __PACKAGE__ . "::\u$rdbms_module";
402 0           my $module_to_load = $module_name;
403 0           $module_to_load =~ s/::/\//g;
404 0           eval { require "${module_to_load}.pm" };
  0            
405 0 0         return if $@;
406 0           $loaded_modules{$rdbms_module} = $module_name;
407 0           $module_name;
408             }
409             }
410              
411             =item connect
412              
413             Connects to the database.
414              
415             =cut
416              
417             sub connect {
418 0     0 1   my ($self) = @_;
419 0 0         my $dbh = DBI->connect(
420             $self->dsn,
421             $self->username,
422             $self->password,
423             { PrintError => 0, AutoCommit => 1},
424             ) or $self->error_handler("Cannot connect to database " . $self->dsn . " " . $DBI::errstr);
425 0           $dbh->{Warn} = 0;
426 0           $self->set_dbh($dbh);
427 0           $self->is_connected(1);
428             }
429              
430              
431             =item check_connection
432              
433             Checks the database connection and reconnects if necessary.
434              
435             =cut
436              
437             sub check_connection {
438 0     0 1   my ($self) = @_;
439 0 0         unless (eval { $self->dbh->ping }) {
  0            
440 0           warn "Database disconnected, reconnecting\n";
441 0           $self->connect;
442             }
443             }
444              
445              
446             =item do
447              
448             Executes passed in sql statement.
449              
450             =cut
451              
452             sub do {
453 0     0 1   my ($self, $sql) = @_;
454 0           $self->record_action_start_time;
455 0 0         $self->dbh->do($sql)
456             or $self->error_handler($sql);
457 0           $self->record_action_end_time($sql, 'execute');
458             }
459              
460              
461             =item sql_handler
462              
463             Returns a new sql handeler instance.
464              
465             my $sql_handler = $connection->sql_handler(
466             name => 'emp_ins'
467             sql => "INSERT INTO emp(empno, ename) VALUES(?, ?)",
468             );
469             $sql_handler->execute(1, 'Smith');
470              
471             =cut
472              
473             sub sql_handler {
474 0     0 1   my ($self, %args) = @_;
475 0   0       my $name = $args{name} || $args{sql};
476 0           my $result = $self->_sql_handler($name);
477 0 0 0       if(! $result || $self->no_cache) {
478 0           $result = DBIx::SQLHandler->new(connection => $self, %args);
479 0           $self->_sql_handler($name, $result);
480             }
481 0           $result;
482             }
483              
484              
485             =item find_sql_handler
486              
487             Returns cached sql handler.
488             Takes sql handler name as parameter.
489              
490              
491             my $sql_handler = $connection->find_sql_handler('emp_ins');
492             $sql_handler->execute(1, 'Scott');
493              
494              
495             =cut
496              
497             sub find_sql_handler {
498 0     0 1   my ($self, $name) = @_;
499 0           $self->_sql_handler($name);
500             }
501              
502              
503             =item execute_statement
504              
505             Executes passed in statement.
506              
507             $connection->execute_statement("INSERT INTO emp(empno, ename) VALUES(?, ?)", 1, 'Smith');
508              
509             =cut
510              
511             sub execute_statement {
512 0     0 1   my ($self, $sql, @bind_variables) = @_;
513 0           my $sql_handler = $self->sql_handler(sql => $sql);
514 0           $sql_handler->execute(@bind_variables);
515             }
516              
517              
518             =item query_cursor
519              
520             my $cursor = $connection->query_cursor(sql => "SELECT * FROM emp WHERE empno = ?");
521             my @result_set;
522             $cursor->execute([1], \@result_set);
523              
524             or # my $result_set = $cursor->execute([1]);
525              
526             my $iterator = $cursor->iterator;
527             while($iterator->()) {
528             #do some stuff
529             #@result_set
530             }
531              
532             # or
533              
534             while($cusor->fetch()) {
535             #do some stuff
536             #@result_set
537             }
538              
539             =cut
540              
541             sub query_cursor {
542 0     0 1   my ($self, %args) = @_;
543 0   0       my $name = $args{name} || $args{sql};
544 0           my $result = $self->_query_cursor($name);
545 0 0 0       if(! $result || $self->no_cache) {
546 0           $result = DBIx::QueryCursor->new(connection => $self, %args);
547 0           $self->_query_cursor($name, $result);
548             }
549 0           $result;
550             }
551              
552              
553             =item find_query_cursor
554              
555             Returns cached query cursor.
556             Takes query cursor name as parmeter.
557              
558             my $cursor = $connection->find_query_cursor('my_cusror');
559             my $result_set = $cursor->execute([1]);
560              
561             =cut
562              
563             sub find_query_cursor {
564 0     0 1   my ($self, $name) = @_;
565 0           $self->_query_cursor($name);
566             }
567              
568              
569             =item plsql_handler
570              
571             Returns a new plsql handeler instance .
572             Takes DBIx::PLSQLHandler constructor parameters.
573              
574             my $plsql_handler = $connection->plsql_handler(
575             name => 'my_plsql',
576             plsql => "DECLARE
577             debit_amt CONSTANT NUMBER(5,2) := 500.00;
578             BEGIN
579             SELECT a.bal INTO :acct_balance FROM accounts a
580             WHERE a.account_id = :acct AND a.debit > debit_amt;
581             :extra_info := 'debit_amt: ' || debit_amt;
582             END;");
583              
584             my $result_set = $plsql_handler->execute(acct => 000212);
585             print $result_set->{acct_balance};
586             print $result_set->{extra_info};
587              
588             =cut
589              
590             sub plsql_handler {
591 0     0 1   my ($self, %args) = @_;
592 0   0       my $name = $args{name} || $args{sql};
593 0           my $result = $self->_plsql_handler($name);
594 0 0 0       if(! $result || $self->no_cache) {
595 0           $result = DBIx::PLSQLHandler->new(connection => $self, %args);
596 0           $self->_plsql_handler($name, $result);
597             }
598 0           $result;
599            
600             }
601              
602              
603             =item find_plsql_handler
604              
605             Returns cached plsql handler, takes name of handler.
606              
607             my $plsql_handler = $connection->find_plsql_handler('my_plsql');
608             my $result_set = $plsql_handler->execute(acct => 000212);
609              
610             =cut
611              
612             sub find_plsql_handler {
613 0     0 1   my ($self, $name) = @_;
614 0           $self->_plsql_handler($name);
615             }
616              
617              
618             =item record
619              
620             Returns resultset record. Takes sql statement, and bind variables parameters as list.
621              
622             my $resultset = $connection->record("SELECT * FROM emp WHERE ename = ? AND deptno = ? ", 'scott', 10);
623             #$resultset->{ename}
624             # do some stuff
625              
626             =cut
627              
628             sub record {
629 0     0 1   my ($self, $sql, @bind_variables) = @_;
630 0           my $query_cursor = $self->query_cursor(sql => $sql);
631 0           my $result = $query_cursor->execute(\@bind_variables);
632 0           $query_cursor->fetch();
633 0           $result;
634             }
635              
636              
637             =item begin_work
638              
639             Begins transaction.
640              
641             =cut
642              
643             sub begin_work {
644 0     0 1   my ($self) = @_;
645 0           my $dbh = $self->dbh;
646 0 0         confess "connection has allready active transaction "
647             if $self->_active_transaction;
648 0           $self->_active_transaction(1);
649 0 0         my $result = $dbh->begin_work()
650             or $self->error_handler("Could not start transaction");
651             }
652              
653              
654             =item commit
655              
656             Commits current transaction.
657              
658             =cut
659              
660             sub commit {
661 0     0 1   my ($self) = @_;
662 0           my $dbh = $self->dbh;
663 0           $self->_active_transaction(0);
664 0 0         $dbh->commit()
665             or $self->error_handler("Could not commit current transaction");
666             }
667              
668              
669             =item rollback
670              
671             Rollbacks current transaction.
672              
673             =cut
674              
675             sub rollback {
676 0     0 1   my ($self) = @_;
677 0           my $dbh = $self->dbh;
678 0           $self->_active_transaction(0);
679 0 0         $dbh->rollback()
680             or $self->error_handler("Could not rollback current transaction");
681             }
682              
683              
684              
685             {
686             my %connections;
687             my %connections_counter;
688              
689              
690             =item initialise
691            
692             Initialises connection.
693              
694             =cut
695              
696             sub initialise {
697 0     0 1   my ($self) = @_;
698 0 0         $self->set_name($self->dsn . " " . $self->username) unless $self->name;
699 0 0         if($self->dbh) {
700 0           $self->is_connected(1);
701             } else {
702 0           $self->connect;
703             }
704 0           $self->set_session_variables($self->db_session_variables)
705 0 0         if (keys %{$self->db_session_variables});
706 0           $self->_cache_connection;
707             }
708              
709              
710              
711             =item connection
712              
713             Returns connection object for passed in connection name.
714              
715             =cut
716              
717             sub connection {
718 0     0 1   my ($class, $name) = @_;
719 0 0         if(!exists($connections_counter{$name})) {
720 0           die "connection $name does not exist";
721             }
722 0           my $result;
723 0 0         if ($CONNECTION_POOLING) {
724 0           $result = $connections{"${name}_0"}->_find_connection;
725 0           $result->_check_connection;
726 0           $result->last_in_use(time);
727            
728             } else {
729 0           $result = $connections{"${name}_0"};
730             }
731 0           $class->check_connnections;
732 0           $result;
733             }
734              
735              
736             =item has_autocomit_mode
737              
738             Returns true if connection has autocommit mode
739              
740             =cut
741              
742             sub has_autocomit_mode {
743 0     0 1   my ($self) = @_;
744 0           !! $self->dbh->{AutoCommit};
745             }
746              
747              
748             =item _find_connection
749              
750             Finds connections
751              
752             =cut
753              
754             sub _find_connection {
755 0     0     my ($self) = @_;
756 0           my $name = $self->name;
757 0 0         unless ($self->in_use) {
758 0           $self->set_in_use(1);
759 0           return $self;
760             }
761 0           my $counter = $connections_counter{$name};
762 0           for my $i(0 .. $counter) {
763 0           my $connection = $connections{"${name}_$i"};
764 0 0         unless ($connection->in_use) {
765 0           $connection->set_in_use(1);
766 0           return $connection;
767             }
768             }
769 0           $self->_clone_connection();
770             }
771              
772              
773             =item _cache_connection
774              
775             Checks connection
776              
777             =cut
778              
779             sub _cache_connection {
780 0     0     my ($self) = @_;
781 0           my $name = $self->name;
782 0 0         my $counter = exists $connections_counter{$name} ? $connections_counter{$name} + 1 : 0;
783 0           $connections_counter{$name} = $counter;
784 0           $connections{"${name}_${counter}"} = $self;
785             }
786              
787              
788             =item _clone_connection
789              
790             Clones current connection. Returns a new connection object.
791              
792             =cut
793              
794             sub _clone_connection {
795 0     0     my ($self) = @_;
796 0           my $connection = __PACKAGE__->new(
797             name => $self->name,
798             dsn => $self->dsn,
799             username => $self->username,
800             password => $self->password,
801             );
802 0           $connection->set_in_use(1);
803 0           $connection;
804             }
805              
806              
807             =item _check_connection
808              
809             Checks connection state.
810              
811             =cut
812              
813             sub _check_connection {
814 0     0     my ($self) = @_;
815 0 0         return $self->connect unless $self->is_connected;
816 0 0         if($self->_is_idled) {
817 0           $self->check_connection;
818             }
819             }
820            
821            
822             =item _is_idled
823              
824             returns true if connection is idle.
825              
826             =cut
827              
828              
829             sub _is_idled {
830 0     0     my $self = shift;
831 0   0       !! (time - ($self->last_in_use || time) > $IDLE_THRESHOLD);
832             }
833              
834              
835             =item check_connnections
836              
837             Checks all connection and disconnects all inactive for longer the 5 mins
838              
839             =cut
840              
841             sub check_connnections {
842 0     0 1   my ($class) = @_;
843 0           for my $k(keys %connections) {
844 0           my $connection = $connections{$k};
845 0 0 0       $connection->disconnect() if(! $connection->in_use && $connection->_is_idled);
846             }
847             }
848            
849              
850             =item close
851              
852             Returns connection to the connection pool,
853             so that connection may be reused by another call Connection->connection('connection_name')
854             rather then its clone.
855              
856             =cut
857              
858             sub close {
859 0     0 1   my ($self) = @_;
860 0 0         if ($CONNECTION_POOLING) {
861 0           $self->set_in_use(0);
862 0           $self->last_in_use(time);
863             }
864             }
865              
866             }
867              
868              
869             =item disconnect
870              
871             Disconnects from current database.
872              
873             =cut
874              
875             sub disconnect {
876 0     0 1   my ($self) = @_;
877 0 0         my $dbh = $self->dbh or return;
878 0           $self->set_query_cursors({});
879 0           $self->sql_handlers({});
880 0           $self->is_connected(0);
881 0 0         $self->dbh->disconnect
882             or $self->error_handler("Can not disconnect from database: $DBI::errstr");
883            
884             }
885              
886              
887             =item dbms_name
888              
889             Returns database name
890              
891             =cut
892              
893             sub dbms_name {
894 0     0 1   my ($self) = @_;
895 0           $self->dbh->get_info($GetInfoType{SQL_DBMS_NAME});
896             }
897              
898              
899             =item dbms_version
900              
901             Returns database version
902              
903             =cut
904              
905             sub dbms_version {
906 0     0 1   my ($self) = @_;
907 0           $self->dbh->get_info($GetInfoType{SQL_DBMS_VER});
908             }
909              
910              
911             =item primary_key_info
912              
913             Returns primary key information, takes table name
914             Return array ref (DBI::primary_key_info)
915              
916             TABLE_CAT: The catalog identifier. This field is NULL (undef) if not applicable to the data source, which is often the case. This field is empty if not applicable to the table.
917              
918             TABLE_SCHEM: The schema identifier. This field is NULL (undef) if not applicable to the data source, and empty if not applicable to the table.
919              
920             TABLE_NAME: The table identifier.
921              
922             COLUMN_NAME: The column identifier.
923              
924             KEY_SEQ: The column sequence number (starting with 1). Note: This field is named ORDINAL_POSITION in SQL/CLI.
925              
926             PK_NAME The primary key constraint identifier. This field is NULL (undef) if not applicable to the data source.
927              
928             =cut
929              
930             sub primary_key_info {
931 0     0 1   my ($self, $table_name, $schema) = @_;
932 0           my $sth = $self->dbh->primary_key_info(undef, $schema, $table_name);
933 0 0         my $result = $sth ? $sth->fetchall_arrayref : undef;
934 0 0 0       if($result && ! @$result) {
935 0           my $module_name = $self->load_module('SQL');
936 0 0 0       if($module_name && $module_name->can('primary_key_info')) {
937 0           my $sql = $module_name->primary_key_info($schema);
938 0           my $cursor = $self->query_cursor(sql => $sql);
939 0 0         my $resultset = $cursor->execute([$table_name, ($schema ? $schema : ())]);
940 0           $result = [];
941 0           while ($cursor->fetch()) {
942 0           push @$result, [undef, $schema, $resultset->{table_name}, $resultset->{column_name}, undef, $resultset->{pk_name}];
943             }
944             }
945             }
946 0           $result;
947             }
948              
949              
950             =item primary_key_columns
951              
952             Returns primary key columns
953              
954             my @primary_key_columns = $connection->primary_key_columns('emp');
955              
956             =cut
957              
958             sub primary_key_columns {
959 0     0 1   my ($self, $table_name) = @_;
960 0           my ($schema, $table) = ($table_name =~ m/([^\.]+)\.(.+)/);
961 0 0         my $info = $self->primary_key_info($schema ? ($schema, $table) : ($table_name));
962 0           map { $_->[3] } @$info;
  0            
963             }
964              
965              
966             =item table_info
967              
968             Returns table info.
969             See also DBI::table_info
970              
971             =cut
972              
973             sub table_info {
974 0     0 1   my ($self, $table_name) = @_;
975 0           my $sth = $self->dbh->table_info(undef, undef, $table_name, 'TABLE');
976 0           my $result = $sth->fetchall_arrayref;
977 0 0         unless (@$result) {
978 0           my $module_name = $self->load_module('SQL');
979 0 0 0       if ($module_name && $module_name->can('has_table')) {
980 0           $result = $module_name->has_table($self, $table_name);
981              
982             }
983             }
984 0           $result;
985             }
986              
987             =item set_session_variables
988              
989             =cut
990              
991             sub set_session_variables {
992 0     0 1   my ($self, $db_session_variables) = @_;
993 0           my $module_name = $self->load_module('SQL');
994 0 0 0       if ($module_name && $module_name->can('set_session_variables')) {
995 0           $module_name->set_session_variables($self, $db_session_variables);
996             }
997             }
998              
999              
1000             =item has_table
1001              
1002             Returns true if table exists in database schema
1003              
1004             =cut
1005              
1006             sub has_table {
1007 0     0 1   my ($self, $table_name) = @_;
1008 0           my $result = $self->table_info($table_name);
1009 0           !! @$result;
1010             }
1011              
1012              
1013             =item has_sequence
1014              
1015             Returns true if has sequence
1016              
1017             =cut
1018              
1019             sub has_sequence {
1020 0     0 1   my ($self, $sequence_name) = @_;
1021 0           my $result;
1022 0           my $module_name = $self->load_module('SQL');
1023 0 0 0       if($module_name && $module_name->can('has_sequence')) {
1024 0           my $record = $self->record($module_name->has_sequence($self->username), $sequence_name);
1025 0           $result = $record->{sequence_name};
1026             } else {
1027 0           warn "not implemented ${module_name}::has_sequence";
1028             }
1029 0           $result;
1030             }
1031              
1032              
1033             =item sequence_value
1034              
1035             Returns sequence's value. Takes seqence name.
1036              
1037             $connection->sequence_value('emp_seq');
1038              
1039             =cut
1040              
1041             sub sequence_value {
1042 0     0 1   my ($self, $name) = @_;
1043 0           my $module_name = $self->load_module('SQL');
1044 0           my $sql = $module_name->sequence_value($name);
1045 0           my ($result) = $self->record($sql);
1046 0           $result->{val};
1047             }
1048              
1049              
1050             =item reset_sequence
1051              
1052             Restart sequence. Takes sequence name, initial sequence value, incremental sequence value.
1053              
1054             $connection->reset_sequence('emp_seq', 1, 1);
1055              
1056             =cut
1057              
1058             sub reset_sequence {
1059 0     0 1   my ($self, $name, $start_with, $increment_by) = @_;
1060 0   0       $start_with ||= 1;
1061 0   0       $increment_by ||= 1;
1062 0           my $module_name = $self->load_module('SQL');
1063 0 0 0       if($module_name && $module_name->can('reset_sequence')) {
1064 0           my @sqls = $module_name->reset_sequence($name, $start_with, $increment_by, $self);
1065 0           $self->do($_) for @sqls;
1066             } else {
1067 0           warn "not implemented ${module_name}::reset_sequence";
1068             }
1069             }
1070              
1071              
1072             =item record_action_start_time
1073              
1074             Records database operation start time.
1075              
1076             =cut
1077              
1078             sub record_action_start_time {
1079 0     0 1   my $self = shift;
1080 0 0         return unless $self->collect_statistics;
1081 0           $self->action_start_time([gettimeofday])
1082             }
1083              
1084              
1085             =item record_action_end_time
1086              
1087             Records database operation end time.
1088              
1089             =cut
1090              
1091             sub record_action_end_time {
1092 0     0 1   my ($self, $name, $method) = @_;
1093 0 0         return unless $self->collect_statistics;
1094 0   0       $method ||= [split /::/, [caller(1)]->[3]]->[-1];
1095 0           my $duration = tv_interval($self->action_start_time, [gettimeofday]);
1096 0           my $tracking = $self->tracking;
1097 0   0       my $info = $tracking->{$name} ||= {};
1098 0   0       my $stats = $info->{$method} ||= {};
1099 0 0         unless (exists($info->{" called"})) {
1100 0           $stats->{" called"} = 1;
1101 0           $stats->{min} = $duration;
1102 0           $stats->{max} = $duration;
1103 0           $stats->{avg} = $duration;
1104             } else {
1105 0           $stats->{" called"}++;
1106 0 0         $stats->{min} = $duration if $info->{min} > $duration;
1107 0 0         $stats->{max} = $duration if $info->{max} < $duration;
1108 0           $stats->{avg} = ($info->{avg} + $duration) / 2.0
1109             }
1110             }
1111              
1112              
1113             =item format_usage_report
1114              
1115             Formats usage report.
1116              
1117             =cut
1118              
1119             sub format_usage_report {
1120 0     0 1   my $self = shift;
1121 0 0         return unless $self->collect_statistics;
1122 0           my $tracking = $self->tracking;
1123 0           my $footer = "";
1124 0           my $body = "";
1125 0           my $i = 0;
1126 0           foreach my $k (sort keys %$tracking) {
1127 0           $footer .= "$i: \n " . $k;
1128 0           $body .= "SQL id $i:\n";
1129            
1130 0           my $item = $tracking->{$k};
1131 0           foreach my $j(sort keys %$item) {
1132 0           $body .= " $j => " ;
1133 0           my $details = $item->{$j};
1134 0           foreach my $m (sort keys %$details) {
1135 0           $body .= " $m = " . $details->{$m}
1136             }
1137 0           $body .= "\n";
1138             }
1139 0           $body .= "\n";
1140 0           $i++;
1141             }
1142 0           return $self->name . " USAGE REPORT\n" . $body . "SQL:\n" . $footer;
1143             }
1144              
1145              
1146              
1147             =item print_usage_report
1148              
1149             Prints usage report to stander output.
1150              
1151             =cut
1152              
1153             sub print_usage_report {
1154 0     0 1   my ($self, $fh) = @_;
1155 0           print $fh $self->format_usage_report;
1156             }
1157              
1158              
1159             =item print_usage_report_to_file
1160              
1161             Prints usage report to file
1162              
1163             =cut
1164              
1165             sub print_usage_report_to_file {
1166 0     0 1   my ($self) = @_;
1167 0           my $dir = $self->statistics_dir;
1168 0 0 0       if($self->collect_statistics && -d $dir) {
1169 0           my $file = $dir . $self->name . "." . $$;
1170 0 0         open my $fh, '>>', $file
1171             or die "cant open file $file";
1172 0           $self->print_usage_report;
1173 0           ::close($fh);
1174             }
1175             }
1176              
1177             =item error_handler
1178              
1179             Returns error message, takes error message, and optionally bind variables.
1180             If bind variables are passed in the sql's place holders are replaced with the bind_variables.
1181              
1182             =cut
1183              
1184             sub error_handler {
1185 0     0 1   my ($self, $sql, $sql_handler) = @_;
1186 0           my $dbh = $self->dbh;
1187 0 0         my $message = "[" . $self->name ."]: " . $sql . '\': ' .($dbh ? $dbh->errstr : '');
1188 0 0         if ($self->custom_error_handler) {
1189 0           $self->custom_error_handler->($self, $message, $sql_handler);
1190             } else {
1191 0           confess $message;
1192             }
1193             }
1194              
1195              
1196             =item update_lob
1197              
1198             Updates lob.
1199              
1200             Takes table_name, lob column name, lob content, hash_ref to primary key values. optionally lob size column name.
1201              
1202             $connection->update_lob(lob_test => 'blob_content', $lob_content, {id => 1}, 'doc_size');
1203              
1204             =cut
1205              
1206             sub update_lob {
1207 0     0 1   my ($self, $table_name, $lob_column_name, $lob, $primary_key_values, $lob_size_column_name) = @_;
1208 0           my $module_name = $self->load_module('SQL');
1209 0 0 0       if($module_name && $module_name->can('update_lob')) {
1210 0           $module_name->update_lob($self, $table_name, $lob_column_name, $lob, $primary_key_values, $lob_size_column_name);
1211             } else {
1212 0           warn "not implemented ${module_name}::update_lob";
1213             }
1214             }
1215              
1216             =item fetch_lob
1217              
1218             Returns lob, takes table name, lob column name, hash ref of primary key values, lob size column name
1219              
1220             my $lob = $connection->fetch_lob(lob_test => 'blob_content', {id => 1}, 'doc_size');
1221              
1222             =cut
1223              
1224             sub fetch_lob {
1225 0     0 1   my ($self, $table_name, $lob_column_name, $primary_key_values, $lob_size_column_name) = @_;
1226 0           my $result;
1227 0           my $module_name = $self->load_module('SQL');
1228 0 0 0       if($module_name && $module_name->can('fetch_lob')) {
1229 0           $result = $module_name->fetch_lob($self, $table_name, $lob_column_name, $primary_key_values, $lob_size_column_name);
1230             } else {
1231 0           warn "not implemented ${module_name}::fetch_lob";
1232             }
1233 0           $result;
1234             }
1235              
1236              
1237             =item _where_clause
1238              
1239             Returns Where caluse sql fragment, takes hash ref of fields values.
1240              
1241             =cut
1242              
1243             sub _where_clause {
1244 0     0     my ($self, $field_values) = @_;
1245 0           " WHERE " . join(" AND ", map {( $_ . ' = ? ')} sort keys %$field_values);
  0            
1246             }
1247              
1248              
1249             =item DESTORY
1250              
1251             =cut
1252              
1253             sub DESTORY {
1254 0     0 1   my ($self) = @_;
1255 0           $self->print_usage_report_to_file;
1256             }
1257              
1258              
1259             1;
1260              
1261             __END__