File Coverage

blib/lib/DBIx/BLOB/Handle.pm
Criterion Covered Total %
statement 23 123 18.7
branch 1 46 2.1
condition 0 31 0.0
subroutine 8 17 47.0
pod 1 1 100.0
total 33 218 15.1


line stmt bran cond sub pod time code
1             package DBIx::BLOB::Handle;
2              
3 1     1   606 use base qw( IO::Handle IO::Seekable );
  1         2  
  1         982  
4 1     1   8219 use strict;
  1         2  
  1         36  
5 1     1   6 use vars qw( $VERSION );
  1         6  
  1         59  
6 1     1   6 use warnings;
  1         1  
  1         31  
7 1     1   6 use Symbol;
  1         2  
  1         51  
8 1     1   2295 use DBI;
  1         20873  
  1         126  
9              
10             $VERSION = '0.2';
11              
12             sub import {
13 1     1   10 my $class = shift;
14 1 50       1987 if( grep { $_ eq ':INTO_STATEMENT' } @_ ){
  0            
15             # Danger! Pretend the DBI statement class can provide blobs as handles
16 1     1   14 no warnings;
  1         2  
  1         1328  
17             *DBI::st::blob_as_handle = sub {
18 0     0     return "$class"->new(@_);
19 0           };
20             }
21             }
22              
23             # required is the DBI statement
24             # optional is the 0 based column index that contains the blob (default = 0)
25             # optional is the blocksize to be read from the database (default = 4096)
26             sub new {
27 0     0 1   my($self, $sth, $field, $blocksize) = @_;
28 0   0       $self = ref $self || $self;
29 0           my $s = Symbol::gensym;
30 0           tie $$s,$self,$sth,$field,$blocksize;
31 0           return bless $s, $self;
32             }
33              
34             sub TIEHANDLE {
35 0     0     my $class = shift;
36 0   0       return bless {sth => shift
      0        
37             ,field => shift || 0
38             ,blocksize => shift || 4096
39             ,pos => 0
40             ,eof => 0
41             ,line => undef
42             ,lines_ref => []
43             },$class;
44             }
45              
46             sub READLINE {
47 0     0     my $self = shift;
48 0           my $buf;
49 0   0       ( my $sep = $/ ) ||= '';
50 0 0         unless( $sep ){ # more efficient if we want to slurp the whole lot
    0          
51 0           my @frags;
52 0           while( ! $self->{eof} ){
53 0           READ($self, $buf);
54 0 0         last if $self->{eof};
55 0           push @frags, $buf;
56             }
57 0           $. = ++$self->{line};
58 0 0         wantarray ? return ( join('',@frags) ) : return join('',@frags);
59             }
60             elsif(wantarray){
61 0           while( ! $self->{eof} ){
62 0           READ($self,$buf);
63 0           my $lines = pop( @{$self->{lines_ref}} ) . $buf;
  0            
64 0           push @{$self->{lines_ref}}, $lines =~ /(.*?$sep|.+)/gs;
  0            
65             }
66 0           $. = $self->{line} = scalar @{$self->{lines_ref}};
  0            
67 0           return @{ delete $self->{lines_ref} };
  0            
68             }else{
69 0           while(1){
70 0 0 0       if( ( @{$self->{lines_ref}} > 1 ) || $self->{eof} ){
  0            
71 0 0         $. = ++$self->{line} if @{$self->{lines_ref}};
  0            
72 0           return shift @{$self->{lines_ref}};
  0            
73             }
74 0           READ($self,$buf);
75 0           my $lines = pop( @{$self->{lines_ref}} ) . $buf;
  0            
76 0           push @{$self->{lines_ref}}, $lines =~ /(.*?$sep|.+)/gs;
  0            
77             }
78             }
79             }
80              
81             sub TELL {
82 0     0     return $_[0]->{pos};
83             }
84              
85             sub EOF {
86 0     0     return $_[0]->{eof};
87             }
88              
89             sub GETC {
90 0     0     my $self = shift;
91 0           my($len,$buf) = (0,'');
92 0           $self->{sth}->blob_read($self->{field}, $self->{pos}, 1,\$buf);
93 0           $len = length $buf;
94 0 0         if( $len ){
95 0           $self->{pos} += $len;
96 0           return $buf;
97             }else{
98 0           $self->{eof} = 1;
99 0           return undef;
100             }
101             }
102              
103             sub READ {
104 0     0     my($self,undef,$length,$offset) = @_;
105 0 0 0       $length ||= $self->{blocksize} unless defined $length;
106 0 0         die "Negative length" unless $length >= 0; # like the built in read does
107 0   0       $offset ||= 0;
108 0 0 0       if( defined($_[1]) && ( ( $offset > length($_[1]) ) || ( $offset < 0 ) ) ){
      0        
109 0           die "Offset outside string"; # like the built in read does
110             }
111 0           my($len,$buf) = (0,'');
112 0           $self->{sth}->blob_read($self->{field}, $self->{pos}, $length,\$buf);
113             # The 5 argument form of blob_read appears to be broken
114             # (dies when called as such) otherwise we could do
115             # $h->blob_read($field, $offset, $len [, \$buf [, $bufoffset]])
116             # and then we wouldn't have to do the substring manipulation of $_[1]
117 0           $len = length $buf;
118 0 0         if($len){
119 0           $self->{pos} += $len;
120             }else{
121 0           $self->{eof} = 1;
122             }
123 0   0       $_[1] ||= ''; # avoids substr error
124 0           substr($_[1],$offset) = $buf;
125 0           return $len;
126             }
127              
128             sub SEEK {
129 0     0     my($self,$offset,$whence) = @_;
130 0 0         if( $whence == IO::Seekable::SEEK_SET() ){
    0          
    0          
131 0 0         if( $offset > 0 ){
132 0 0         if( $offset > $self->{pos} ){
133 0           $offset -= $self->{pos};
134 0   0       while( ! $self->{eof} && ( $self->{pos} < $offset ) ){
135 0           READ($self, undef, undef);
136             }
137 0 0         if( $offset < $self->{pos} ){
138 0           $self->{pos} = $offset;
139 0           $self->{eof} = 0;
140             }
141             }else{
142 0           $self->{pos} = $offset;
143 0           $self->{eof} = 0;
144             }
145             }else{
146 0           $self->{pos} = 0;
147 0           $self->{eof} = 0;
148             }
149             }
150             elsif( $whence == IO::Seekable::SEEK_CUR() ){
151 0 0         if( $offset < 0 ){
152 0           $self->{pos} += $offset;
153 0 0         $self->{pos} = 0 if $self->{pos} < 0;
154 0           $self->{eof} = 0;
155             }else{
156 0           my $seekto = $self->{pos} + $offset;
157 0   0       while( ! $self->{eof} && ( $self->{pos} < $seekto ) ){
158 0           READ($self, undef, undef);
159             }
160 0 0         if( $seekto < $self->{pos} ){
161 0           $self->{pos} = $seekto;
162 0           $self->{eof} = 0;
163             }
164             }
165             }
166             elsif( $whence == IO::Seekable::SEEK_END() ){
167 0           while( ! $self->{eof} ){
168 0           READ($self, undef, undef);
169             }
170 0 0         if( $offset < 0 ){
171 0           $self->{eof} = 0; # reset eof
172 0           $self->{pos} += $offset;
173 0 0         $self->{pos} = 0 if $self->{pos} < 0;
174             }
175             }
176 0           return $self->{pos}; # tell
177             }
178              
179             1;
180              
181             __END__