File Coverage

blib/lib/DBIx/Interp.pm
Criterion Covered Total %
statement 137 228 60.0
branch 23 66 34.8
condition 3 12 25.0
subroutine 32 52 61.5
pod 4 4 100.0
total 199 362 54.9


line stmt bran cond sub pod time code
1             package DBIx::Interp;
2              
3 2     2   85939 use strict;
  2         3  
  2         50  
4 2     2   7 use warnings;
  2         4  
  2         43  
5 2     2   7 use Carp;
  2         3  
  2         94  
6 2     2   460 use SQL::Interp ':all';
  2         3  
  2         9  
7 2     2   930 use base 'DBI';
  2         4  
  2         1335  
8 2         11 use Sub::Exporter -setup => {
9             exports => [
10             qw{attr dbi_interp key_field},
11             qw{ sql_interp
12             sql_interp_strict
13             sql_type
14             sql },
15             ],
16 2     2   13967 };
  2         3  
17              
18             our $VERSION = '1.25';
19              
20             our @CARP_NOT =
21             qw(DBIx::Interp DBIx::Interp::db DBIx::Interp::STX);
22              
23             sub key_field {
24 0     0 1 0 my $key = shift;
25 0         0 return bless \$key, "DBIx::Interp::Key";
26             }
27              
28             sub attr {
29 0     0 1 0 return bless {@_}, "DBIx::Interp::Attr";
30             }
31              
32             sub dbi_interp {
33 0     0 1 0 return DBIx::Interp::db::dbi_interp(@_);
34             }
35              
36             sub new {
37 1     1 1 562 shift;
38 1         5 return DBIx::Interp::db->new(@_);
39             }
40              
41             sub _wrap(&) {
42 16     16   24 my $code = shift;
43 16         21 my $x;
44             my @x;
45 16         19 my $want = wantarray();
46 16         20 eval {
47 16 100       23 if ($want) { @x = $code->(); }
  4         6  
48 12         18 else { $x = $code->(); }
49             };
50 16 50       607 if ($@) { croak $@; }
  0         0  
51 16 100       46 return $want ? @x : $x;
52             }
53              
54             #####
55             package DBIx::Interp::db;
56 2     2   1216 use strict;
  2         4  
  2         51  
57 2     2   9 use warnings;
  2         2  
  2         55  
58 2     2   9 use Carp;
  2         3  
  2         96  
59 2     2   10 use base 'DBI::db';
  2         2  
  2         549  
60 2     2   13 use Scalar::Util 'weaken';
  2         3  
  2         2465  
61              
62             our @CARP_NOT = @DBIx::Interp::CARP_NOT;
63              
64             my $priv = 'private_DBIxInterpolate';
65              
66             sub new {
67 1     1   2 my $class = shift;
68 1         1 my $dbh;
69              
70 1 50       5 if (UNIVERSAL::isa($_[0], 'DBI::db')) {
    0          
71 1         2 $dbh = shift;
72             }
73             elsif (ref $_[0] eq 'ARRAY') {
74 0         0 $dbh = DBI->connect(@{shift @_});
  0         0  
75 0 0       0 return if ! defined $dbh;
76             }
77             else {
78 0         0 croak 'DBIx::Interp::db::new() not passed database connection';
79             }
80              
81 1   33     7 my $interp = SQL::Interp->new(($dbh || ()), @_);
82 1         2 my $self = $dbh;
83 1         2 bless $self, $class;
84 1         7 my $private = $self->{$priv} = {};
85 1         23 $private->{stx} = $self->prepare_i();
86 1         2 $private->{interp} = $interp;
87              
88             # weaken circular references to allow garbage collection
89 1         6 weaken $private->{stx}->{dbx};
90 1         4 weaken $private->{interp}->{dbh};
91              
92 1         3 return $self;
93             }
94              
95             #sub DESTROY {
96             # my ($self) = @_;
97             # $self->SUPER::DESTROY();
98             #}
99              
100             sub connect {
101 0     0   0 my $class = shift;
102 0         0 my $self;
103 0         0 eval {
104 0         0 my $dbh = DBI->connect(@_);
105 0 0       0 return if ! $dbh;
106 0         0 $self = DBIx::Interp->new($dbh); #Q: OK?
107             };
108 0 0       0 if ($@) { croak $@; }
  0         0  
109 0         0 return $self;
110             }
111              
112             # removed in 0.40:
113             #sub dbh
114              
115              
116             # new in 0.31
117             sub stx {
118 0     0   0 my $self = shift;
119 0         0 return $self->{$priv}->{stx};
120             }
121              
122             # new in 0.40
123             sub interp {
124 8     8   9 my $self = shift;
125 8         47 return $self->{$priv}->{interp};
126             }
127              
128             sub dbi_interp {
129 8     8   11 my $key;
130             my $attr;
131             my @args = grep {
132 8         20 my $save = 1;
  36         40  
133 36 50       68 if (ref($_) eq 'DBIx::Interp::Key') {
    50          
134 0         0 $key = $_; $save = 0;
  0         0  
135             }
136             elsif (ref($_) eq 'DBIx::Interp::Attr') {
137 0         0 $attr = {%$_}; $save = 0;
  0         0  
138             }
139 36         52 $save;
140             } @_;
141 8         16 my ($sql, @bind) = sql_interp(@args);
142 8         33 my @params = ($sql);
143 8 50       14 push @params, $$key if defined $key;
144 8         24 push @params, $attr, @bind;
145 8         25 return @params;
146             }
147              
148             sub sql_interp {
149 8     8   21 my (@params) = @_;
150 8 50       25 if (UNIVERSAL::isa($_[0], 'DBIx::Interp::db')) {
151 8         11 my $self = shift;
152 8         13 return SQL::Interp::sql_interp($self->interp(), @_);
153             }
154             else {
155 0         0 return SQL::Interp::sql_interp(@_);
156             }
157             }
158              
159             # based on function in DBI
160             sub _do_selectrow_i {
161 0     0   0 my ($self, $method, @list) = @_;
162              
163             #my $sth = $dbh->prepare($stmt, $attr) or return;
164             #_do_execute($sth, @bind) or return;
165 0         0 my $stx = $self->{$priv}->{stx};
166 0 0       0 $stx->execute_i(@list) or return;
167 0         0 my $sth = $stx->sth();
168 0 0       0 my $row = $sth->$method() and $sth->finish;
169 0         0 return $row;
170             }
171              
172             # new in 0.40
173             sub prepare_i {
174 2     2   905 my ($self) = @_;
175 2         8 return DBIx::Interp::STX->new($self);
176             }
177              
178             # new in 0.40
179             sub do_i {
180 0     0   0 my ($self, @list) = @_;
181 0         0 return _wrap {
182             # based on DBI::do
183             # my $sth = $dbh->prepare($sql, $attr) or return undef;
184             # _do_execute(@bind) or return undef;
185 0         0 my $stx = $self->{$priv}->{stx};
186 0 0       0 $stx->execute_i(@list) or return undef;
187 0         0 my $sth = $stx->sth();
188 0         0 my $rows = $sth->rows;
189 0 0       0 return ($rows == 0) ? "0E0" : $rows;
190             };
191             }
192              
193             # new in 0.40
194             sub selectrow_array_i {
195 0     0   0 my ($self, @list) = @_;
196 0         0 my $want = wantarray;
197 0         0 return _wrap {
198             # based on DBI::selectrow_array
199              
200 0 0       0 my $row = $self->_do_selectrow_i('fetchrow_arrayref', @list)
201             or return;
202 0 0       0 return $row->[0] unless $want;
203 0         0 return @$row;
204             };
205             }
206              
207             # new in 0.40
208             sub selectrow_arrayref_i {
209 0     0   0 my ($self, @list) = @_;
210 0         0 return _wrap {
211             # based on DBI::selectrow_arrayref
212              
213 0         0 return $self->_do_selectrow_i('fetchrow_arrayref', @list);
214             };
215             }
216              
217             # new in 0.40
218             sub selectrow_hashref_i {
219 0     0   0 my ($self, @list) = @_;
220 0         0 return _wrap {
221             # based on DBI::selectrow_hashref
222              
223 0         0 return $self->_do_selectrow_i('fetchrow_hashref', @list);
224             };
225             }
226              
227             # new in 0.40
228             sub selectall_arrayref_i {
229 2     2   39 my ($self, @list) = @_;
230 2         2 return _wrap {
231             # improve: no need to to a full dbi_interp call here and elsewhere
232 2         5 my ($sql, $attr, @bind) = $self->dbi_interp(@list); # need $attr
233              
234             # based on DBI::selectall_arrayref
235             # my $sth = $dbh->prepare($sql, $attr) or return;
236             # _do_execute($sth, @bind) or return;
237              
238 2         13 my $stx = $self->{$priv}->{stx};
239 2 50       8 $stx->execute_i(@list) or return;
240 2         15 my $sth = $stx->sth();
241             # typically undef, else hash or array ref
242 2         5 my $slice = $attr->{Slice};
243 2 50 33     9 if (!$slice and $slice=$attr->{Columns}) {
244 0 0       0 if (ref $slice eq 'ARRAY') {
245 0         0 $slice = [ @{$attr->{Columns}} ];
  0         0  
246 0         0 for (@$slice) { $_-- }
  0         0  
247             }
248             }
249             my $rows = $sth->fetchall_arrayref(
250 2         11 $slice, my $MaxRows = $attr->{MaxRows});
251 2 50       173 $sth->finish if defined $MaxRows;
252 2         14 return $rows;
253             };
254             }
255              
256             # new in 0.40
257             sub selectall_hashref_i {
258 0     0   0 my ($self, @list) = @_;
259 0         0 return _wrap {
260             #need $key_field
261 0         0 my ($sql, $key_field, $attr, @bind) = $self->dbi_interp(@list);
262              
263             # based on DBI::selectall_hashref
264             # my $sth = $dbh->prepare($sql, $attr);
265             # return unless $sth;
266             # _do_execute($sth, @bind) or return;
267              
268 0         0 my $stx = $self->{$priv}->{stx};
269 0 0       0 $stx->execute_i(@list) or return;
270 0         0 my $sth = $stx->sth();
271 0         0 return $sth->fetchall_hashref($key_field);
272             };
273             }
274              
275             # new in 0.40
276             sub selectcol_arrayref_i {
277 0     0   0 my ($self, @list) = @_;
278 0         0 return _wrap {
279 0         0 my ($sql, $attr, @bind) = $self->dbi_interp(@list); # need $attr
280              
281             # based on DBI::selectcol_arrayref
282             # my $sth = $dbh->prepare($sql, $attr);
283             # return unless $sth;
284             # _do_execute($sth, @bind) or return;
285              
286 0         0 my $stx = $self->{$priv}->{stx};
287 0 0       0 $stx->execute_i(@list) or return;
288 0 0       0 my @columns = ($attr->{Columns}) ? @{$attr->{Columns}} : (1);
  0         0  
289 0         0 my @values = (undef) x @columns;
290 0         0 my $idx = 0;
291 0         0 my $sth = $stx->sth();
292 0         0 for (@columns) {
293 0 0       0 $sth->bind_col($_, \$values[$idx++]) or return;
294             }
295 0         0 my @col;
296 0 0       0 if (my $max = $attr->{MaxRows}) {
297 0   0     0 push @col, @values while @col<$max && $sth->fetch;
298             }
299             else {
300 0         0 push @col, @values while $sth->fetch;
301             }
302 0         0 return \@col;
303             };
304             }
305              
306             1;
307              
308             #####
309             package DBIx::Interp::STX;
310 2     2   13 use strict;
  2         3  
  2         44  
311 2     2   9 use warnings;
  2         2  
  2         53  
312 2     2   8 use Carp;
  2         5  
  2         1508  
313              
314             our @CARP_NOT = @DBIx::Interp::CARP_NOT;
315              
316             sub new {
317 2     2   3 my ($class, $dbx) = @_;
318 2         8 my $self = bless {
319             # active sth
320             sth => undef,
321              
322             # map: SQL --> sth (sth cache)
323             sths => {},
324              
325             # queue of SQL. used to select sth to delete if cache is full
326             sql_queue => [],
327              
328             # DBIx::Interp
329             dbx => $dbx,
330              
331             # max sths allowed in the cache
332             max_sths => 1
333             }, $class;
334 2         5 return $self;
335             }
336              
337             sub max_sths {
338 2     2   435 my ($self, $max_sths) = @_;
339 2 100       6 if (defined $max_sths) {
340 1         2 $self->{max_sths} = $max_sths;
341             }
342             else {
343 1         3 return $self->{max_sths};
344             }
345             }
346              
347             sub sth {
348 8     8   13 my $self = shift;
349 8         31 return $self->{sth};
350             }
351              
352             sub sths {
353 2     2   3 my $self = shift;
354 2         4 return {%{$self->{sths}}};
  2         13  
355             }
356              
357             # renamed execute --> execute_i in 0.40
358             sub execute_i {
359 6     6   1040 my ($self, @list) = @_;
360              
361             return DBIx::Interp::_wrap {
362 6     6   16 my ($sql, @bind) = $self->{dbx}->dbi_interp(@list);
363 6 50 33     14 shift @bind if defined $bind[0] && ref $bind[0] eq ''; # remove any key_field()
364 6         7 my $attr = shift @bind;
365 6         11 my $sth = $self->{sths}->{$sql};
366 6 100       10 if (! defined $sth) {
367 5 50       24 $sth = $self->{dbx}->prepare($sql, $attr) or return;
368 5 100       568 if (@{$self->{sql_queue}} + 1 > $self->{max_sths}) {
  5         14  
369 2         4 my $sql_remove = shift @{$self->{sql_queue}};
  2         2  
370 2         11 delete $self->{sths}->{$sql_remove};
371             }
372 5         21 $self->{sths}->{$sql} = $sth;
373 5         7 push @{$self->{sql_queue}}, $sql;
  5         9  
374             }
375 6         10 $self->{sth} = $sth;
376 6         20 _bind_params($sth, @bind);
377 6         34 return $sth->execute();
378 6         30 };
379             }
380              
381             sub _bind_params {
382 6     6   12 my ($sth, @bind) = @_;
383 6         8 my $num = 1;
384             return DBIx::Interp::_wrap {
385 6 100   6   19 if (ref($bind[0]) eq 'ARRAY') {
386 1         2 for my $val (@bind) {
387 6         63 $sth->bind_param($num++, $val->[0], $val->[1]->{type});
388             }
389             }
390             else {
391 5         10 for my $val (@bind) {
392 13         133 $sth->bind_param($num++, $val);
393             }
394             }
395 6         23 };
396             }
397              
398             sub fetchrow_arrayref {
399 0     0   0 my $self = shift;
400             return DBIx::Interp::_wrap {
401 0     0   0 return $self->{sth}->fetchrow_arrayref();
402 0         0 };
403             }
404              
405             sub fetchrow_array {
406 0     0   0 my $self = shift;
407             return DBIx::Interp::_wrap {
408 0     0   0 return $self->{sth}->fetchrow_array();
409 0         0 };
410             }
411              
412             sub fetchrow_hashref {
413 0     0   0 my ($self, @params) = @_;
414             return DBIx::Interp::_wrap {
415 0     0   0 return $self->{sth}->fetchrow_hashref(@params);
416 0         0 };
417             }
418              
419             sub fetchall_arrayref {
420 4     4   33 my ($self, @params) = @_;
421             return DBIx::Interp::_wrap {
422 4     4   11 return $self->{sth}->fetchall_arrayref(@params);
423 4         12 };
424             }
425              
426             sub fetchall_hashref {
427 0     0     my ($self, @params) = @_;
428             return DBIx::Interp::_wrap {
429 0     0     return $self->{sth}->fetchall_hashref(@params);
430 0           };
431             }
432              
433             1;
434              
435             __END__