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