File Coverage

blib/lib/DBIx/Interp.pm
Criterion Covered Total %
statement 136 227 59.9
branch 23 66 34.8
condition 2 9 22.2
subroutine 32 52 61.5
pod 4 4 100.0
total 197 358 55.0


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