File Coverage

blib/lib/CGI/Session/Driver/DBI.pm
Criterion Covered Total %
statement 22 90 24.4
branch 1 48 2.0
condition 2 21 9.5
subroutine 7 12 58.3
pod 5 6 83.3
total 37 177 20.9


line stmt bran cond sub pod time code
1             package CGI::Session::Driver::DBI;
2              
3             # $Id$
4              
5 1     1   70168 use strict;
  1         3  
  1         55  
6              
7 1     1   15 use DBI;
  1         2  
  1         66  
8 1     1   7 use Carp;
  1         2  
  1         100  
9 1     1   943 use CGI::Session::Driver;
  1         2  
  1         273  
10              
11             @CGI::Session::Driver::DBI::ISA = ( "CGI::Session::Driver" );
12             $CGI::Session::Driver::DBI::VERSION = '4.43';
13              
14              
15             sub init {
16 0     0 1 0 my $self = shift;
17 0 0       0 if ( defined $self->{Handle} ) {
18 0 0       0 if (ref $self->{Handle} eq 'CODE') {
19 0         0 $self->{Handle} = $self->{Handle}->();
20             }
21             else {
22             # We assume the handle is working, and there is nothing to do.
23             }
24             }
25             else {
26 0         0 $self->{Handle} = DBI->connect(
27             $self->{DataSource}, $self->{User}, $self->{Password},
28             { RaiseError=>1, PrintError=>1, AutoCommit=>1 }
29             );
30 0 0       0 unless ( $self->{Handle} ) {
31 0         0 return $self->set_error( "init(): couldn't connect to database: " . DBI->errstr );
32             }
33 0         0 $self->{_disconnect} = 1;
34             }
35 0         0 return 1;
36             }
37              
38             # A setter/accessor method for the table name, defaulting to 'sessions'
39              
40             sub table_name {
41 0     0 0 0 my $self = shift;
42 0   0     0 my $class = ref( $self ) || $self;
43              
44 0 0 0     0 if ( (@_ == 0) && ref($self) && ($self->{TableName}) ) {
      0        
45 0         0 return $self->{TableName};
46             }
47              
48 1     1   7 no strict 'refs';
  1         2  
  1         1082  
49 0 0       0 if ( @_ ) {
50 0         0 $self->{TableName} = shift;
51             }
52              
53 0 0       0 unless (defined $self->{TableName}) {
54 0         0 $self->{TableName} = "sessions";
55             }
56              
57 0         0 return $self->{TableName};
58             }
59              
60              
61             sub retrieve {
62 1     1 1 16 my $self = shift;
63 1         3 my ($sid) = @_;
64 1 50       267 croak "retrieve(): usage error" unless $sid;
65              
66              
67 0         0 my $dbh = $self->{Handle};
68 0         0 my $sth = $dbh->prepare_cached("SELECT $self->{DataColName} FROM " . $self->table_name . " WHERE $self->{IdColName}=?", undef, 3);
69 0 0       0 unless ( $sth ) {
70 0         0 return $self->set_error( "retrieve(): DBI->prepare failed with error message " . $dbh->errstr );
71             }
72 0 0       0 $sth->execute( $sid ) or return $self->set_error( "retrieve(): \$sth->execute failed with error message " . $sth->errstr);
73              
74 0         0 my ($row) = $sth->fetchrow_array();
75              
76 0         0 $sth->finish;
77              
78 0 0       0 return 0 unless $row;
79 0         0 return $row;
80             }
81              
82              
83             sub store {
84             # die;
85 0     0 1 0 my $self = shift;
86 0         0 my ($sid, $datastr) = @_;
87 0 0 0     0 croak "store(): usage error" unless $sid && $datastr;
88              
89              
90 0         0 my $dbh = $self->{Handle};
91 0         0 my $sth = $dbh->prepare_cached("SELECT $self->{IdColName} FROM " . $self->table_name . " WHERE $self->{IdColName}=?", undef, 3);
92 0 0       0 unless ( defined $sth ) {
93 0         0 return $self->set_error( "store(): \$dbh->prepare failed with message " . $sth->errstr );
94             }
95              
96 0 0       0 $sth->execute( $sid ) or return $self->set_error( "store(): \$sth->execute failed with message " . $sth->errstr );
97 0         0 my $rc = $sth->fetchrow_array;
98 0         0 $sth->finish;
99              
100 0         0 my $action_sth;
101 0 0       0 if ( $rc ) {
102 0         0 $action_sth = $dbh->prepare_cached("UPDATE " . $self->table_name . " SET $self->{DataColName}=? WHERE $self->{IdColName}=?", undef, 3);
103             } else {
104 0         0 $action_sth = $dbh->prepare_cached("INSERT INTO " . $self->table_name . " ($self->{DataColName}, $self->{IdColName}) VALUES(?, ?)", undef, 3);
105             }
106            
107 0 0       0 unless ( defined $action_sth ) {
108 0         0 return $self->set_error( "store(): \$dbh->prepare failed with message " . $dbh->errstr );
109             }
110 0 0       0 $action_sth->execute($datastr, $sid)
111             or return $self->set_error( "store(): \$action_sth->execute failed " . $action_sth->errstr );
112              
113 0         0 $action_sth->finish;
114              
115 0         0 return 1;
116             }
117              
118              
119             sub remove {
120 0     0 1 0 my $self = shift;
121 0         0 my ($sid) = @_;
122 0 0       0 croak "remove(): usage error" unless $sid;
123              
124 0         0 my $rc = $self->{Handle}->do( 'DELETE FROM ' . $self->table_name . " WHERE $self->{IdColName}= ?", {}, $sid );
125 0 0       0 unless ( $rc ) {
126 0         0 croak "remove(): \$dbh->do failed!";
127             }
128            
129 0         0 return 1;
130             }
131              
132              
133             sub DESTROY {
134 0     0   0 my $self = shift;
135              
136 0 0 0     0 unless ( defined $self->{Handle} && $self->{Handle} -> ping ) {
137 0         0 $self->set_error(__PACKAGE__ . '::DESTROY(). Database handle has gone away');
138 0         0 return;
139             }
140              
141 0 0       0 unless ( $self->{Handle}->{AutoCommit} ) {
142 0         0 $self->{Handle}->commit;
143             }
144 0 0       0 if ( $self->{_disconnect} ) {
145 0         0 $self->{Handle}->disconnect;
146             }
147             }
148              
149              
150             sub traverse {
151 1     1 1 935 my $self = shift;
152 1         2 my ($coderef) = @_;
153              
154 1 0 33     9 unless ( $coderef && ref( $coderef ) && (ref $coderef eq 'CODE') ) {
      33        
155 1         161 croak "traverse(): usage error";
156             }
157              
158 0           my $tablename = $self->table_name();
159 0 0         my $sth = $self->{Handle}->prepare_cached("SELECT $self->{IdColName} FROM $tablename", undef, 3)
160             or return $self->set_error("traverse(): couldn't prepare SQL statement. " . $self->{Handle}->errstr);
161 0 0         $sth->execute() or return $self->set_error("traverse(): couldn't execute statement $sth->{Statement}. " . $sth->errstr);
162              
163 0           while ( my ($sid) = $sth->fetchrow_array ) {
164 0           $coderef->($sid);
165             }
166              
167 0           $sth->finish;
168              
169 0           return 1;
170             }
171              
172              
173             1;
174              
175             =pod
176              
177             =head1 NAME
178              
179             CGI::Session::Driver::DBI - Base class for native DBI-related CGI::Session drivers
180              
181             =head1 SYNOPSIS
182              
183             require CGI::Session::Driver::DBI;
184             @ISA = qw( CGI::Session::Driver::DBI );
185              
186             =head1 DESCRIPTION
187              
188             In most cases you can create a new DBI-driven CGI::Session driver by simply creating an empty driver file that inherits from CGI::Session::Driver::DBI. That's exactly what L<sqlite|CGI::Session::Driver::sqlite> does. The only reason why this class doesn't suit for a valid driver is its name isn't in lowercase. I'm serious!
189              
190             =head2 NOTES
191              
192             CGI::Session::Driver::DBI defines init() method, which makes DBI handle available for drivers in I<Handle> - object attribute regardless of what C<\%dsn_args> were used in creating session object. Should your driver require non-standard initialization you have to re-define init() method in your F<.pm> file, but make sure to set 'Handle' - object attribute to database handle (returned by DBI->connect(...)) if you wish to inherit any of the methods from CGI::Session::Driver::DBI.
193              
194             =head1 STORAGE
195              
196             Before you can use any DBI-based session drivers you need to make sure compatible database table is created for CGI::Session to work with. Following command will produce minimal requirements in most SQL databases:
197              
198             CREATE TABLE sessions (
199             id CHAR(32) NOT NULL PRIMARY KEY,
200             a_session TEXT NOT NULL
201             );
202              
203             Your session table can define additional columns, but the above two are required. Name of the session table is expected to be I<sessions> by default. You may use a different name if you wish. To do this you have to pass I<TableName> as part of your C< \%dsn_args >:
204              
205             $s = CGI::Session->new('driver:sqlite', undef, {TableName=>'my_sessions'});
206             $s = CGI::Session->new('driver:mysql', undef,
207             {
208             TableName=>'my_sessions',
209             DataSource=>'dbi:mysql:shopping_cart'.
210             });
211              
212             To use different column names, change the 'create table' statement, and then simply do this:
213              
214             $s = CGI::Session->new('driver:pg', undef,
215             {
216             TableName=>'session',
217             IdColName=>'my_id',
218             DataColName=>'my_data',
219             DataSource=>'dbi:pg:dbname=project',
220             });
221              
222             or
223              
224             $s = CGI::Session->new('driver:pg', undef,
225             {
226             TableName=>'session',
227             IdColName=>'my_id',
228             DataColName=>'my_data',
229             Handle=>$dbh,
230             });
231              
232             =head1 DRIVER ARGUMENTS
233              
234             Following driver arguments are supported:
235              
236             =over 4
237              
238             =item DataSource
239              
240             First argument to be passed to L<DBI|DBI>->L<connect()|DBI/connect()>. If the driver makes
241             the database connection itself, it will also explicitly disconnect from the database when
242             the driver object is DESTROYed.
243              
244             =item User
245              
246             User privileged to connect to the database defined in C<DataSource>.
247              
248             =item Password
249              
250             Password of the I<User> privileged to connect to the database defined in C<DataSource>
251              
252             =item Handle
253              
254             An existing L<DBI> database handle object. The handle can be created on demand
255             by providing a code reference as a argument, such as C<<sub{DBI->connect}>>.
256             This way, the database connection is only created if it actually needed. This can be useful
257             when combined with a framework plugin like L<CGI::Application::Plugin::Session>, which creates
258             a CGI::Session object on demand as well.
259              
260             C<Handle> will override all the above arguments, if any present.
261              
262             =item TableName
263              
264             Name of the table session data will be stored in.
265              
266             =back
267              
268             =head1 LICENSING
269              
270             For support and licensing information see L<CGI::Session|CGI::Session>
271              
272             =cut
273