File Coverage

blib/lib/Template/Plugin/DBI.pm
Criterion Covered Total %
statement 154 225 68.4
branch 43 88 48.8
condition 29 78 37.1
subroutine 33 43 76.7
pod 12 13 92.3
total 271 447 60.6


line stmt bran cond sub pod time code
1             #==============================================================================
2             #
3             # Template::Plugin::DBI
4             #
5             # DESCRIPTION
6             # A Template Toolkit plugin to provide access to a DBI data source.
7             #
8             # AUTHORS
9             # Original version by Simon Matthews
10             # with some reworking by Andy Wardley and other
11             # contributions from Craig Barratt ,
12             # Dave Hodgkinson and Rafael Kitover
13             # .
14             # Since 2010 Jens Rehsack maintains this module.
15             #
16             # COPYRIGHT
17             # Copyright (C) 1999-2000 Simon Matthews. All Rights Reserved.
18             # Copyright (C) 2006 Andy Wardley. All Rights Reserved.
19             # Copyright (C) 2010 Jens Rehsack. All Rights Reserved.
20             #
21             # This module is free software; you can redistribute it and/or
22             # modify it under the same terms as Perl itself.
23             #
24             # REVISION
25             # $Id$
26             #
27             #==============================================================================
28              
29             package Template::Plugin::DBI;
30              
31 1     1   6321 use strict;
  1         3  
  1         66  
32 1     1   8 use warnings;
  1         2  
  1         40  
33              
34 1     1   19 use Carp qw(croak carp);
  1         2  
  1         89  
35 1     1   12 use DBI;
  1         2  
  1         40  
36              
37 1     1   6 use Template::Exception;
  1         1  
  1         35  
38 1     1   6 use base 'Template::Plugin';
  1         1  
  1         985  
39              
40             our $VERSION = 2.65;
41             our $DEBUG = 0 unless defined $DEBUG;
42             our $QUERY = 'Template::Plugin::DBI::Query';
43             our $ITERATOR = 'Template::Plugin::DBI::Iterator';
44              
45             my $threads_enabled = 0;
46             my $have_tvr = 0;
47              
48             BEGIN
49             {
50 1 50   1   936 $INC{"threads.pm"} and eval {
51 0         0 ++$threads_enabled;
52 0         0 require threads::variable::reap;
53 0         0 ++$have_tvr;
54             };
55              
56 1 50       8649 $have_tvr
57             and threads::variable::reap->import(qw(reap reapref));
58             }
59              
60             # alias _connect() to connect() for backwards compatability
61             *_connect = \*connect;
62              
63             #------------------------------------------------------------------------
64             # new($context, @params)
65             #
66             # Constructor which returns a reference to a new DBI plugin object.
67             # A connection string (dsn), user name and password may be passed as
68             # positional arguments or a hash array of connection parameters can be
69             # passed to initialise a connection. Otherwise, an unconnected DBI
70             # plugin object is returned.
71             #------------------------------------------------------------------------
72              
73             sub new
74             {
75 26     26 1 646689 my ( $class, $context, @connect_args ) = @_;
76 26 50       227 my $self =
77             ref $class
78             ? $class
79             : bless {
80             _CONTEXT => $context,
81             _STH => undef,
82             }, $class;
83              
84 26 100       165 $self->connect(@connect_args) if @connect_args;
85              
86 26         99 return $self;
87             }
88              
89             #------------------------------------------------------------------------
90             # connect( $data_source, $username, $password, $attributes )
91             # connect( { data_source => 'dbi:driver:database'
92             # username => 'foo'
93             # password => 'bar' } )
94             #
95             # Opens a DBI connection for the plugin.
96             #------------------------------------------------------------------------
97              
98             sub connect
99             {
100 29     29 1 1317 my $self = shift;
101 29 100       147 my $params = ref $_[-1] eq 'HASH' ? pop(@_) : {};
102 29         48 my ( $dbh, $dsn, $user, $pass, $klobs );
103              
104             # set debug flag
105 29 50       112 exists $params->{debug} and $DEBUG = $params->{debug};
106 29   50     247 $self->{_DEBUG} = $params->{debug} || 0;
107              
108             # fetch 'dbh' named paramater or use positional arguments or named
109             # parameters to specify 'dsn', 'user' and 'pass'
110              
111 29 100       111 if ( $dbh = $params->{dbh} )
112             {
113             # disconnect any existing database handle that we previously opened
114 1 50 33     15 $self->{_DBH}->disconnect()
115             if $self->{_DBH} && $self->{_DBH_CONNECT};
116              
117             # store new dbh but leave _DBH_CONNECT false to prevent us
118             # from automatically closing it in the future
119 1         3 $self->{_DBH} = $dbh;
120 1         4 $self->{_DBH_CONNECT} = 0;
121             }
122             else
123             {
124             # certain Perl programmers are known to have problems with short
125             # term memory loss (see Tie::Hash::Cannabinol) so we let the poor
126             # blighters fumble any kind of argument that looks like it might
127             # identify the database
128              
129 28   50     234 $dsn =
130             shift
131             || delete $params->{data_source}
132             || delete $params->{database}
133             || delete $params->{connect}
134             || delete $params->{dsn}
135             || delete $params->{db}
136             || delete $self->{_DSN}
137             || $ENV{DBI_DSN}
138             || return $self->_throw('data source not defined');
139              
140             # add 'dbi:' prefix if it's not there
141 26 100       134 $dsn = "dbi:$dsn" unless $dsn =~ /^dbi:/i;
142              
143 26   33     284 $user =
144             shift
145             || delete $params->{username}
146             || delete $params->{user}
147             || delete $self->{_USER};
148              
149 26   33     307 $pass =
150             shift
151             || delete $params->{password}
152             || delete $params->{pass}
153             || delete $self->{_PASS};
154              
155 26   50     113 $user ||= '';
156 26   50     94 $pass ||= '';
157              
158             # save connection data because we might need it later to do a tie()
159 26         156 @$self{qw( _DSN _USER _PASS )} = ( $dsn, $user, $pass );
160              
161             # reuse existing database handle if connection params match
162 26         131 my $connect = join( ':', $dsn, $user, $pass );
163 26 50 66     89 return ''
164             if $self->connected() && $self->{_DBH_CONNECT} eq $connect;
165              
166             # otherwise disconnect any existing database handle that we opened
167 26         99 $self->disconnect();
168              
169             # don't need DBI to automatically print errors because all calls go
170             # via this plugin interface and we always check return values
171 26 100       99 $params->{PrintError} = 0
172             unless defined $params->{PrintError};
173 26 100       89 $params->{RaiseError} = 0
174             unless defined $params->{RaiseError};
175              
176 26   50     276 $self->{_DBH} = DBI->connect_cached( $dsn, $user, $pass, $params )
177             || return $self->_throw("DBI connect failed: $DBI::errstr");
178              
179             # store the connection parameters
180 26         35989 $self->{_DBH_CONNECT} = $connect;
181             }
182              
183 27 50 33     127 $have_tvr
184             and reap( $self->{_DBH} )
185             and reap( $self->{_STH} );
186              
187 27         85 return '';
188             }
189              
190             sub connected
191             {
192 80     80 0 135 my $self = $_[0];
193 80   66     883 return $self->{_DBH} && $self->{_DBH}->isa('DBI::db');
194             }
195              
196             #------------------------------------------------------------------------
197             # disconnect()
198             #
199             # Disconnects the current active database connection.
200             #------------------------------------------------------------------------
201              
202             sub disconnect
203             {
204 28     28 1 284 my $self = $_[0];
205 28         81 delete( $self->{_STH} ); # first DESTROY any queries
206 28 100       172 $self->connected() and $self->{_DBH}->disconnect();
207 28         145 delete $self->{_DBH};
208 28         48 delete $self->{_DSN};
209 28         54 delete $self->{_USER};
210 28         37 delete $self->{_PASS};
211 28         43 delete $self->{_DBH_CONNECT};
212 28         55 return '';
213             }
214              
215             #------------------------------------------------------------------------
216             # tie( $table, $key )
217             #
218             # Return a hash tied to a table in the database, indexed by the specified
219             # key.
220             #------------------------------------------------------------------------
221              
222             sub tie
223             {
224 0     0 1 0 my $self = shift;
225 0 0       0 my $params = ref $_[-1] eq 'HASH' ? pop(@_) : {};
226 0         0 my ( $table, $key, $klobs, $debug, %hash );
227              
228 0         0 eval { require Tie::DBI };
  0         0  
229 0 0       0 $self->_throw("failed to load Tie::DBI module: $@") if $@;
230              
231 0   0     0 $table =
232             shift
233             || $params->{table}
234             || $self->_throw('table not defined');
235              
236 0   0     0 $key =
237             shift
238             || $params->{key}
239             || $self->_throw('key not defined');
240              
241             # Achtung der Klobberman!
242 0         0 $klobs = $params->{clobber};
243 0 0       0 $klobs = $params->{CLOBBER} unless defined $klobs;
244              
245             # going the extra mile to allow user to use UPPER or lower case or
246             # inherit internel debug flag set by connect()
247 0         0 $debug = $params->{debug};
248 0 0       0 $debug = $params->{DEBUG} unless defined $debug;
249 0 0       0 $debug = $self->{_DEBUG} unless defined $debug;
250              
251 0   0     0 tie %hash, 'Tie::DBI', {
      0        
      0        
252             %$params, # any other Tie::DBI options like DEBUG, WARN, etc
253             db => $self->{_DBH} || $self->{_DSN},
254             user => $self->{_USER},
255             password => $self->{_PASS},
256             table => $table,
257             key => $key,
258             CLOBBER => $klobs || 0,
259             DEBUG => $debug || 0,
260             };
261              
262 0         0 return \%hash;
263             }
264              
265             #------------------------------------------------------------------------
266             # prepare($sql)
267             #
268             # Prepare a query and store the live statement handle internally for
269             # subsequent execute() calls.
270             #------------------------------------------------------------------------
271              
272             sub prepare
273             {
274 31     31 1 620 my ($self,@args) = @_;
275 31 50 33     273 unless( @args and $args[0] and "" eq ref($args[0]) )
      33        
276             {
277 0         0 return $self->_throw("prepare called without statement");
278             }
279              
280 31   50     135 my $sth = $self->dbh()->prepare(@args)
281             || return $self->_throw( "DBI prepare failed: " . $self->dbh()->errstr );
282              
283             # create wrapper object around handle to return to template client
284 29         132295 $sth = $QUERY->new($sth);
285 29         90 $self->{_STH} = $sth;
286              
287 29         165 return $sth;
288             }
289              
290             #------------------------------------------------------------------------
291             # execute()
292             #
293             # Calls execute() on the most recent statement created via prepare().
294             #------------------------------------------------------------------------
295              
296             sub execute
297             {
298 2     2 1 621 my $self = shift;
299 2 50 33     24 my @args = @_ == 1 && ref $_[0] eq 'ARRAY' ? @{ $_[0] } : @_;
  0         0  
300              
301 2   50     8 my $sth = $self->{_STH}
302             || return $self->_throw('no query prepared');
303              
304 2         7 $sth->execute(@args);
305             }
306              
307             #------------------------------------------------------------------------
308             # query($sql, @params)
309             #
310             # Prepares and executes a SQL query.
311             #------------------------------------------------------------------------
312              
313             sub query
314             {
315 26     26 1 19532 my ( $self, $sql, @args ) = @_;
316 26         63 my @prep = ($sql);
317              
318 26 0 33     76 if( @args and $args[0] and ref($args[0]) eq 'HASH' )
      0        
319             {
320 0         0 push(@prep, shift @args);
321             }
322              
323 26         109 return $self->prepare(@prep)->execute(@args);
324             }
325              
326             #------------------------------------------------------------------------
327             # do($sql, \%attr, @bind)
328             #
329             # Prepares and executes a SQL statement.
330             #------------------------------------------------------------------------
331              
332             sub do
333             {
334 2     2 1 187 my $self = shift;
335 2 50 33     21 my @args = @_ == 1 && ref $_[0] eq 'ARRAY' ? @{ $_[0] } : @_;
  0         0  
336              
337 2   33     9 return $self->dbh()->do(@args)
338             || $self->_throw( "DBI do failed: " . $self->dbh()->errstr );
339             }
340              
341             #------------------------------------------------------------------------
342             # quote($value [, $data_type ])
343             #
344             # Returns a quoted string (correct for the connected database) from the
345             # value passed in.
346             #------------------------------------------------------------------------
347              
348             sub quote
349             {
350 0     0 1 0 my ( $self, @quote_args ) = @_;
351 0         0 return $self->dbh()->quote(@quote_args);
352             }
353              
354             #------------------------------------------------------------------------
355             # dbh()
356             #
357             # Internal method to retrieve the database handle belonging to the
358             # instance or attempt to create a new one using connect.
359             #------------------------------------------------------------------------
360              
361             sub dbh
362             {
363 33     33 1 78 my $self = $_[0];
364              
365 33 100       100 $self->{_DBH} or $self->connect;
366              
367 31         271 return $self->{_DBH};
368             }
369              
370             #------------------------------------------------------------------------
371             # DESTROY
372             #
373             # Called automatically when the plugin object goes out of scope to
374             # disconnect the database handle cleanly
375             #------------------------------------------------------------------------
376              
377             sub DESTROY
378             {
379 26     26   5762 my $self = $_[0];
380 26         101 delete( $self->{_STH} ); # first DESTROY any queries
381 26 100 66     2183 $self->{_DBH}->disconnect()
382             if ( $self->connected() && $self->{_DBH_CONNECT} );
383             }
384              
385             #------------------------------------------------------------------------
386             # _throw($error)
387             #
388             # Raise an error by throwing it via die() as a Template::Exception
389             # object of type 'DBI'.
390             #------------------------------------------------------------------------
391              
392             sub _throw
393             {
394 2     2   4 my $self = shift;
395 2   33     8 my $error = shift || croak "DBI throw() called without an error string";
396              
397             # throw error as DBI exception
398 2         23 die( Template::Exception->new( 'DBI', $error ) );
399             }
400              
401             sub fetch
402             {
403 0     0 1 0 my ( $self, $attr ) = @_;
404              
405 0 0       0 return $self->_throw("Cannot fetch attribute on not connected \$dbh") unless ( $self->connected() );
406              
407 0         0 my $dbh = $_[0]->{_DBH};
408 0         0 local $@ = undef;
409 0         0 my $value;
410 0         0 eval { $value = $dbh->{$attr}; };
  0         0  
411 0 0       0 return $self->_throw($@) if ($@);
412              
413 0         0 return $value;
414             }
415              
416             sub store
417             {
418 0     0 1 0 my ( $self, $attr, $value ) = @_;
419              
420 0 0       0 return $self->_throw("Cannot store attribute on not connected \$dbh") unless ( $self->connected() );
421              
422 0         0 my $dbh = $_[0]->{_DBH};
423 0         0 local $@ = undef;
424 0         0 eval { $dbh->{$attr} = $value; };
  0         0  
425 0 0       0 return $self->_throw($@) if ($@);
426              
427 0         0 return 1;
428             }
429              
430             #========================================================================
431             # Template::Plugin::DBI::Query
432             #========================================================================
433              
434             package Template::Plugin::DBI::Query;
435 1     1   18 use vars qw( $DEBUG $ITERATOR $AUTOLOAD );
  1         2  
  1         117  
436              
437 1     1   6 use Scalar::Util qw(blessed);
  1         2  
  1         492  
438              
439             *DEBUG = \$Template::Plugin::DBI::DEBUG;
440             *ITERATOR = \$Template::Plugin::DBI::ITERATOR;
441              
442             sub new
443             {
444 29     29   66 my ( $class, $sth ) = @_;
445 29         179 my $self = bless( { _STH => $sth }, $class );
446             }
447              
448             sub execute
449             {
450 33     33   1475 my $self = shift;
451 33 100 100     318 my @args = @_ == 1 && ref $_[0] eq 'ARRAY' ? @{ $_[0] } : @_;
  1         5  
452              
453 33 50       313 $self->{_STH}->execute(@args)
454             || return Template::Plugin::DBI->_throw( "execute failed: " . $self->{_STH}->errstr );
455              
456 33         111806 $ITERATOR->new($self);
457             }
458              
459             sub DESTROY
460             {
461 29     29   533 undef $_[0]->{_STH};
462             }
463              
464             sub AUTOLOAD
465             {
466 127     127   202 my ( $self, @args ) = @_;
467              
468 127 50       454 die Template::Exception->new( 'DBI', "$self is not an object" ) unless ( blessed($self) );
469 127 50       380 die Template::Exception->new( 'DBI', "No statement handle" ) unless ( defined( $self->{_STH} ) );
470              
471 127         602 ( my $name = $AUTOLOAD ) =~ s/.*://;
472 127         1117 return $self->{_STH}->$name(@args);
473             }
474              
475             #========================================================================
476             # Template::Plugin::DBI::Iterator;
477             #========================================================================
478              
479             package Template::Plugin::DBI::Iterator;
480              
481 1     1   1022 use Template::Iterator;
  1         1542  
  1         31  
482 1     1   7 use base qw( Template::Iterator );
  1         2  
  1         82  
483 1     1   6 use vars qw( $DEBUG );
  1         3  
  1         1061  
484              
485             *DEBUG = \$Template::Plugin::DBI::DEBUG;
486              
487             sub new
488             {
489 33     33   122 my ( $class, $sth, $params ) = @_;
490              
491 33         262 my $rows = $sth->rows();
492              
493 33         394 my $self = bless {
494             _STH => $sth,
495             SIZE => $rows,
496             MAX => $rows - 1,
497             }, $class;
498              
499 33         272 return $self;
500             }
501              
502             #------------------------------------------------------------------------
503             # get_first()
504             #
505             # Initialises iterator to read from statement handle. We maintain a
506             # one-record lookahead buffer to allow us to detect if the current
507             # record is the last in the series.
508             #------------------------------------------------------------------------
509              
510             sub get_first
511             {
512 30     30   1170 my $self = shift;
513 30         88 $self->{_STARTED} = 1;
514              
515             # set some status variables into $self
516 30         248 @$self{qw( PREV ITEM FIRST LAST COUNT INDEX )} = ( undef, undef, 2, 0, 0, -1 );
517              
518             # support 'number' as an alias for 'count' for backwards compatability
519 30         91 $self->{NUMBER} = 0;
520              
521 30 50       113 print STDERR "get_first() called\n" if $DEBUG;
522              
523             # get the first row
524 30         190 $self->_fetchrow();
525              
526 30 50       95 print STDERR "get_first() calling get_next()\n" if $DEBUG;
527              
528 30         81 return $self->get_next();
529             }
530              
531             #------------------------------------------------------------------------
532             # get_next()
533             #
534             # Called to read remaining result records from statement handle.
535             #------------------------------------------------------------------------
536              
537             sub get_next
538             {
539 89     89   12307 my $self = shift;
540 89         88 my ( $data, $fixup );
541              
542             # increment the 'index' and 'count' counts
543 89         153 $self->{INDEX}++;
544 89         112 $self->{COUNT}++;
545 89         102 $self->{NUMBER}++; # 'number' is old name for 'count'
546              
547             # decrement the 'first-record' flag
548 89 100       232 $self->{FIRST}-- if $self->{FIRST};
549              
550             # we should have a row already cache in NEXT
551 89 100       302 return ( undef, Template::Constants::STATUS_DONE )
552             unless $data = $self->{NEXT};
553              
554             # set PREV to be current ITEM from last iteration
555 60         103 $self->{PREV} = $self->{ITEM};
556              
557             # look ahead to the next row so that the rowcache is refilled
558 60         132 $self->_fetchrow();
559              
560 60         152 $self->{ITEM} = $data;
561 60         191 return ( $data, Template::Constants::STATUS_OK );
562             }
563              
564             sub get
565             {
566 1     1   136 my $self = shift;
567 1         2 my ( $data, $error );
568              
569 1 50       12 ( $data, $error ) = $self->{_STARTED} ? $self->get_next() : $self->get_first();
570              
571 1         3 return $data;
572             }
573              
574             sub get_all
575             {
576 2     2   146 my $self = shift;
577 2         6 my $sth = $self->{_STH};
578              
579 2         17 my $data = $sth->fetchall_arrayref( {} );
580 2 50       527 $self->throw( $sth->errstr ) if ( $sth->err() );
581 2 100       9 unshift( @$data, $self->{NEXT} ) if $self->{NEXT};
582 2         6 $self->{LAST} = 1;
583 2         3 $self->{NEXT} = undef;
584              
585 2         8 return $data;
586             }
587              
588             sub get_colnames
589             {
590 0     0   0 my $self = shift;
591 0         0 my $sth = $self->{_STH};
592 0         0 my $error;
593              
594 0         0 my $data = $sth->{_STH}->{NAME_lc};
595 0 0       0 $self->throw( $sth->errstr ) if ( $sth->err() );
596              
597 0         0 return $data;
598             }
599              
600             sub get_COLnames
601             {
602 0     0   0 my $self = shift;
603 0         0 my $sth = $self->{_STH};
604              
605 0         0 my $data = $sth->{_STH}->{NAME_uc};
606 0 0       0 $self->throw( $sth->errstr ) if ( $sth->err() );
607              
608 0         0 return $data;
609             }
610              
611             sub get_all_list
612             {
613 0     0   0 my $self = shift;
614 0         0 my $sth = $self->{_STH};
615              
616 0         0 my $data = $sth->fetchall_arrayref();
617 0 0       0 $self->throw( $sth->errstr ) if ( $sth->err() );
618 0 0       0 if ( $self->{NEXT} )
619             {
620 0         0 my $fetch_hash_key_name = $self->{_STH}->{FetchHashKeyName};
621 0         0 my @cols = @{ $self->{_STH}->{$fetch_hash_key_name} };
  0         0  
622 0         0 my @row = @{ $self->{NEXT} }[@cols];
  0         0  
623 0         0 unshift( @$data, \@row );
624             }
625 0         0 $self->{LAST} = 1;
626 0         0 $self->{NEXT} = undef;
627              
628 0         0 return $data;
629             }
630              
631             sub rows
632             {
633 0     0   0 return $_[0]->{SIZE};
634             }
635              
636             sub fetch
637             {
638 0     0   0 my ( $self, $attr ) = @_;
639 0         0 $self->{_STH}->FETCH($attr);
640             }
641              
642             sub store
643             {
644 0     0   0 my ( $self, $attr, $value ) = @_;
645 0         0 return $self->{_STH}->STORE( $attr, $value );
646             }
647              
648             #------------------------------------------------------------------------
649             # _fetchrow()
650             #
651             # Retrieve a record from the statement handle and store in row cache.
652             #------------------------------------------------------------------------
653              
654             sub _fetchrow
655             {
656 90     90   124 my $self = shift;
657 90         197 my $sth = $self->{_STH};
658              
659             my $data = $sth->fetchrow_hashref() || do
660 90   66     368 {
661             $self->{LAST} = 1;
662             $self->{NEXT} = undef;
663             return;
664             };
665 61         5330 $self->{NEXT} = $data;
666 61         115 return;
667             }
668              
669             1;
670              
671             __END__