File Coverage

blib/lib/SeeAlso/DBI.pm
Criterion Covered Total %
statement 27 163 16.5
branch 0 90 0.0
condition 0 7 0.0
subroutine 9 23 39.1
pod 12 12 100.0
total 48 295 16.2


line stmt bran cond sub pod time code
1 1     1   24027 use strict;
  1         3  
  1         40  
2 1     1   5 use warnings;
  1         2  
  1         57  
3             package SeeAlso::DBI;
4             {
5             $SeeAlso::DBI::VERSION = '0.71';
6             }
7             #ABSTRACT: Store L objects in database.
8              
9 1     1   6886 use DBI;
  1         29456  
  1         71  
10 1     1   989 use DBI::Const::GetInfoType;
  1         7555  
  1         168  
11 1     1   10 use Carp qw(croak);
  1         1  
  1         51  
12              
13 1     1   5 use base qw( SeeAlso::Source );
  1         1  
  1         603  
14 1     1   7 use SeeAlso::Source qw(expand_from_config);
  1         2  
  1         1942  
15              
16             ## no critic
17              
18              
19             sub new {
20 0     0 1   my ($class, %attr) = @_;
21 0           expand_from_config( \%attr, 'DBI' );
22              
23 0 0         if ( $attr{dbi} ) {
24 0 0         $attr{dbi} = 'dbi:' . $attr{dbi} unless $attr{dbi} =~ /^dbi:/i;
25 0 0         $attr{user} = "" unless defined $attr{user};
26 0 0         $attr{password} = "" unless defined $attr{password};
27 0           $attr{dbh} = DBI->connect( $attr{dbi}, $attr{user}, $attr{password} );
28             }
29              
30 0 0         croak('Parameter dbh or dbi required')
31             unless UNIVERSAL::isa( $attr{dbh}, 'DBI::db' );
32 0 0 0       croak('Parameter dbh_ro must be a DBI object')
33             if defined $attr{dbh_ro} and not UNIVERSAL::isa( $attr{dbh_ro}, 'DBI::db' );
34              
35 0           my $self = bless { }, $class;
36              
37 0 0         $self->description( %attr ) if %attr;
38              
39 0           $self->{dbh} = $attr{dbh};
40 0           $self->{dbh_ro} = $attr{dbh_ro};
41 0 0         $self->{table} = defined $attr{table} ? $attr{table} : 'seealso';
42 0   0       $self->{key} = $attr{key} || 'hash';
43              
44 0   0       $self->{idtype} = $attr{idtype} || 'SeeAlso::Identifier';
45 0           eval "require " . $self->{idtype};
46 0 0         croak $@ if $@;
47 0 0         croak($self->{idtype} . ' is not a SeeAlso::Identifier')
48             unless UNIVERSAL::isa( $self->{idtype}, 'SeeAlso::Identifier' );
49              
50             # build SQL strings
51 0           my $table = $self->{dbh}->quote_identifier( $self->{table} );
52 0           my $key = $self->{dbh}->quote_identifier('key');
53 0           my $label = $self->{dbh}->quote_identifier('label');
54 0           my $descr = $self->{dbh}->quote_identifier('description');
55 0           my $uri = $self->{dbh}->quote_identifier('uri');
56 0           my $db_name = $self->{dbh}->get_info( $GetInfoType{SQL_DBMS_NAME} );
57              
58 0           my @values_st;
59 0           my @create_st = ("$key VARCHAR(255)");
60              
61 0 0         if (defined $attr{label}) {
62 0           $self->{label} = $attr{label};
63             } else {
64 0           push @values_st, $label;
65 0           push @create_st, "$label TEXT"
66             }
67 0 0         if (defined $attr{description}) {
68 0           $self->{descr} = $attr{description};
69             } else {
70 0           push @values_st, $descr;
71 0           push @create_st, "$descr TEXT",
72             }
73 0 0         if (defined $attr{uri}) {
74 0           $self->{uri} = $attr{uri};
75             } else {
76 0           push @values_st, $uri;
77 0           push @create_st, "$uri TEXT",
78             }
79              
80 0           my $values = join(", ", @values_st);
81              
82 0           my %sql = (
83             'select' => "SELECT $values FROM $table WHERE $key=?",
84 0           'insert' => "INSERT INTO $table ($key,$values) VALUES (?," . join(",", map {'?'} @values_st) . ")",
85             # update => "UPDATE $table SET $value = ? WHERE $key=?",
86             'delete' => "DELETE FROM $table WHERE $key = ?",
87             'clear' => "DELETE FROM $table",
88             # get_keys => "SELECT DISTINCT $key FROM $table",
89             'create' => "CREATE TABLE IF NOT EXISTS $table (" . join(", ", @create_st) . ")",
90             # TODO: create index:
91             # $dbh->do( 'CREATE INDEX '.$table.'_isbn_idx ON '.$table.' (isbn)' );
92             'drop' => "DROP TABLE $table"
93             );
94              
95 0           foreach my $c ( qw(select insert delete clear create) ) {
96 0 0         $self->{$c} = $attr{$c} ? $attr{$c} : $sql{$c};
97             }
98              
99 0 0         $self->create if $attr{build};
100              
101 0           return $self;
102             }
103              
104              
105             sub query_callback {
106 0     0 1   my ($self, $identifier) = @_;
107              
108 0           my $key = $self->key($identifier);
109              
110 0 0         my $dbh = $self->{dbh_ro} ? $self->{dbh_ro} : $self->{dbh};
111 0 0         my $sth = $dbh->prepare_cached( $self->{'select'} )
112             or croak $dbh->errstr;
113 0 0         $sth->execute($key) or croak $sth->errstr;
114 0           my $result = $sth->fetchall_arrayref;
115              
116 0           my $response = SeeAlso::Response->new( $identifier );
117              
118 0           foreach my $row ( @{$result} ) {
  0            
119 0           my ($label, $description, $uri) = $self->enriched_row( $key, @{$row} );
  0            
120 0           $response->add( $label, $description, $uri );
121             }
122              
123 0           return $response;
124             }
125              
126              
127             sub key {
128 0     0 1   my ($self, $identifier) = @_;
129              
130 0 0         if ( not UNIVERSAL::isa( $identifier, $self->{idtype} ) ) {
131 0           my $class = $self->{idtype};
132 0           $identifier = eval "new $class(\$identifier)"; # TODO: what if this fails?
133             }
134              
135 0 0         if ($self->{key} eq 'hash') {
    0          
    0          
    0          
136 0           return $identifier->hash;
137             } elsif ($self->{key} eq 'value') {
138 0           return $identifier->value;
139             } elsif ($self->{key} eq 'canonical') {
140 0           return $identifier->canonical;
141             } elsif (ref($self->{key}) eq 'CODE') {
142 0           my $code = $self->{key};
143 0           return $code( $identifier );
144             }
145              
146 0           return $identifier->hash;
147             }
148              
149              
150             sub create {
151 0     0 1   my ($self) = @_;
152 0 0         $self->{dbh}->do( $self->{'create'} ) or croak $self->{dbh}->errstr;
153 0           return;
154             }
155              
156              
157             sub clear {
158 0     0 1   my ($self) = @_;
159 0 0         $self->{dbh}->do( $self->{'clear'} ) or croak $self->{dbh}->errstr;
160 0           return;
161             }
162              
163              
164             sub drop {
165 0     0 1   my ($self) = @_;
166 0 0         $self->{dbh}->do( $self->{'drop'} ) or croak $self->{dbh}->errstr;
167 0           return;
168             }
169              
170              
171             sub delete {
172 0     0 1   my ($self, $identifier) = @_;
173 0 0         $self->{dbh}->do( $self->{'delete'}, undef, $self->key($identifier) )
174             or croak $self->{dbh}->errstr;
175             }
176              
177              
178             sub update {
179 0     0 1   my ($self, $response) = @_;
180 0           $self->delete( $response->identifier );
181 0           $self->insert( $response );
182             }
183              
184              
185             sub insert {
186 0     0 1   my ($self, $response) = @_;
187              
188 0 0         croak('SeeAlso::Response object required') unless
189             UNIVERSAL::isa( $response, 'SeeAlso::Response' );
190              
191 0 0         return 0 unless $response->size;
192              
193             # type hash/canonical/value
194 0           my $key = $self->key( $response->identifier );
195 0           my @rows;
196              
197 0           for(my $i=0; $i<$response->size; $i++) {
198 0           my ($label, $description, $uri) = $response->get($i);
199 0           my @insert = ($key);
200 0 0         push @insert, $label unless defined $self->{label};
201 0 0         push @insert, $description unless defined $self->{descr};
202 0 0         push @insert, $uri unless defined $self->{uri};
203 0           push @rows, \@insert;
204             }
205              
206 0     0     return $self->bulk_insert( sub { shift @rows } );
  0            
207             }
208              
209              
210             sub bulk_insert {
211 0     0 1   my ($self, $sub) = @_;
212              
213 0 0         croak('bulk_insert expects a code reference') unless ref($sub) eq 'CODE';
214              
215 0           my $sth = $self->{dbh}->prepare_cached( $self->{insert} );
216 0           my $tuples = $sth->execute_for_fetch( $sub );
217 0           $sth->finish;
218              
219 0           return $tuples;
220             }
221              
222             # ($key,$label,$description,$uri,@row) => ($label,$description,$uri)
223             sub enriched_row {
224 0     0 1   my ($self, @row) = @_;
225              
226 0           my @row2 = @row;
227 0           my $key = shift @row2;
228 0 0         my $label = defined $self->{label} ? $self->{label} : shift @row2;
229 0 0         my $description = defined $self->{descr} ? $self->{descr} : shift @row2;
230 0 0         my $uri = defined $self->{uri} ? $self->{uri} : shift @row2;
231             # code references not supported yet!
232              
233 1     1   8 no warnings;
  1         2  
  1         419  
234 0 0         if ( defined $self->{label} ) {
235 0           $label =~ s/#([0-9])/${row[$1-1]}/g;
236             }
237 0 0         if ( defined $self->{descr} ) {
238 0           $description =~ s/#([0-9])/${row[$1-1]}/g;
239             }
240 0 0         if ( defined $self->{uri} ) {
241 0           $uri =~ s/#([0-9])/${row[$1-1]}/g;
242             }
243              
244 0           return ( $label, $description, $uri );
245             }
246              
247              
248              
249             sub bulk_import {
250 0     0 1   my ($self, %param) = @_;
251 0           my $file = $param{file};
252 0 0         croak 'No file specified' unless defined $file;
253              
254 0 0         my $label = defined $param{label} ? $param{label} : '#2';
255 0 0         my $description = defined $param{descr} ? $param{descr} : '#3';
256 0 0         my $uri = defined $param{uri} ? $param{uri} : '#4';
257              
258 0 0         open FILE, $file or croak "Failed to open file $file";
259 0           binmode FILE, ":utf8";
260              
261             $self->bulk_insert( sub {
262 0     0     my $line = readline(*FILE);
263 0 0         return unless $line;
264 0           chomp($line);
265 0           my @v = split /\t/, $line;
266 0           my ($l,$d,$u) = ($label,$description,$uri);
267              
268 1     1   7 no warnings;
  1         2  
  1         208  
269 0           $l =~ s/#([0-9])/${v[$1-1]}/g;
270 0           $d =~ s/#([0-9])/${v[$1-1]}/g;
271 0           $u =~ s/#([0-9])/${v[$1-1]}/g;
272              
273 0           return [ $v[0], $l, $d, $u ];
274 0           } );
275              
276 0           close FILE;
277             }
278              
279             1;
280              
281              
282             __END__