File Coverage

blib/lib/DBomb/Query.pm
Criterion Covered Total %
statement 103 248 41.5
branch 6 56 10.7
condition 2 6 33.3
subroutine 25 45 55.5
pod 0 30 0.0
total 136 385 35.3


line stmt bran cond sub pod time code
1             package DBomb::Query;
2              
3             =head1 NAME
4              
5             DBomb::Query - A query abstraction.
6              
7             =head1 SYNOPSIS
8              
9             =cut
10              
11 12     12   78055 use strict;
  12         23  
  12         650  
12 12     12   66 use warnings;
  12         99  
  12         468  
13 12     12   16135 use Carp::Assert;
  12         19673  
  12         77  
14 12     12   9285 use DBomb::Conf;
  12         32  
  12         406  
15 12     12   7466 use DBomb::Query::Text;
  12         43  
  12         455  
16 12     12   7430 use DBomb::Query::Expr;
  12         41  
  12         586  
17 12     12   7664 use DBomb::Query::Join;
  12         39  
  12         487  
18 12     12   8476 use DBomb::Query::LeftJoin;
  12         32  
  12         296  
19 12     12   6675 use DBomb::Query::RightJoin;
  12         33  
  12         278  
20 12     12   6806 use DBomb::Query::Limit;
  12         36  
  12         309  
21 12     12   7153 use DBomb::Query::OrderBy;
  12         40  
  12         432  
22 12     12   7366 use DBomb::Query::GroupBy;
  12         35  
  12         365  
23 12     12   80 use DBomb::Util qw(ctx_0);
  12         22  
  12         1228  
24              
25             ## The CVS Revision. See DBomb.pm for the DBomb release version.
26             our $VERSION = '$Revision: 1.22 $';
27              
28             use Class::MethodMaker
29 12         85 'new_with_init' => 'new',
30             'get_set' => [qw(column_names where_obj having_obj orderbys groupbys
31             limit_obj table_sources dbh sth)],
32             'boolean' => [qw(_sql_small_result)]
33 12     12   66 ;
  12         22  
34              
35             ## Lexically scoped unique placeholder value. Used by Expr.
36             my $place_holder = '?';
37 0     0 0 0 sub PlaceHolder { \$place_holder };
38              
39             ## new Query()
40             ## new Query(column_names)
41             ## new Query($dbh,column_names)
42             sub init
43             {
44 12     12 0 1057 my $self = shift;
45 12         394 $self->table_sources([]);
46 12         475 $self->column_names([]);
47 12         440 $self->orderbys([]);
48 12         505 $self->groupbys([]);
49 12         431 $self->where_obj(new DBomb::Query::Expr());
50 12         745 $self->having_obj(new DBomb::Query::Expr());
51              
52             ## Check for a dbh
53 12 50       478 $self->dbh(shift) if UNIVERSAL::isa($_[0],'DBI::db');
54              
55 12 50       55 $self->select(@_) if @_;
56 12         428 return $self;
57             }
58              
59             sub select
60             {
61 12     12 0 17 my $self = shift;
62 12 50       15 push @{$self->column_names}, (ref($_[0]) ? @{$_[0]}: @_);
  12         418  
  0         0  
63 12         125 return $self;
64             }
65              
66             ## mysql-specific extension
67             sub sql_small_result
68             {
69 0     0 0 0 my $self = shift;
70 0         0 $self->_sql_small_result(1);
71 0         0 return $self;
72             }
73              
74             ## from($tables..)
75             sub from
76             {
77 6     6 0 23 my $self = shift;
78 6   33     44 assert(@_ && defined($_[0]), 'valid parameters');
79 6 50       22 push @{$self->table_sources}, map { new DBomb::Query::Text($_)} map { ref($_)? @$_ : $_ } @_;
  6         184  
  6         182  
  6         64  
80 6         52 return $self;
81             }
82              
83             ## join($right)
84             ## join($left, $right)
85             sub join
86             {
87 5     5 0 18 my $self = shift;
88 5         16 $self->_mk_join('DBomb::Query::Join',@_);
89 5         40 return $self;
90             }
91              
92             ## right_join($right)
93             ## right_join($left, $right)
94             sub right_join
95             {
96 1     1 0 6 my $self = shift;
97 1         4 $self->_mk_join('DBomb::Query::RightJoin',@_);
98 1         4 return $self;
99             }
100              
101             ## left_join($right)
102             ## left_join($left, $right)
103             sub left_join
104             {
105 1     1 0 9 my $self = shift;
106 1         5 $self->_mk_join('DBomb::Query::LeftJoin',@_);
107 1         19 return $self;
108             }
109              
110             ## _mk_join($joinclass,$right)
111             ## _mk_join($joinclass,$left, $right)
112             sub _mk_join
113             {
114 7     7   13 my $self = shift;
115 7         10 my $joinclass = shift;
116 7         9 my($left,$right) = @_;
117 7         198 my $table_sources = $self->table_sources;
118 7 100       64 if(@_ == 1){
119 2   33     26 assert(@$table_sources > 0
120             && ( UNIVERSAL::isa(ref($table_sources->[$#$table_sources]),'DBomb::Query::Text')
121             ||UNIVERSAL::isa(ref($table_sources->[$#$table_sources]),'DBomb::Query::Join')
122             ), 'single argument join requires left table (or join)');
123 2         22 $right = $left;
124 2         4 $left = pop @$table_sources;
125             }
126 7         223 push @$table_sources, $joinclass->new($left,$right);
127 7         195 return $self;
128             }
129              
130             sub on
131             {
132 1     1 0 3 my $self = shift;
133 1         29 my $table_sources = $self->table_sources;
134 1         13 assert(@$table_sources, 'ON requires a JOIN');
135 1         10 assert(UNIVERSAL::isa($table_sources->[$#$table_sources],'DBomb::Query::Join'), 'ON requires a JOIN');
136              
137 1         35 $table_sources->[$#$table_sources]->on(new DBomb::Query::Expr(@_));
138 1         9 return $self;
139             }
140              
141             sub using
142             {
143 0     0 0 0 my $self = shift;
144 0         0 my $table_sources = $self->table_sources;
145 0         0 assert(scalar(@_), 'USING requires a list of column names');
146 0         0 assert(UNIVERSAL::isa($table_sources->[$#$table_sources],'DBomb::Query::Join'), 'USING requires a JOIN');
147 0         0 $table_sources->[$#$table_sources]->using(@_);
148 0         0 return $self;
149             }
150              
151             ## where(EXPR, @bind_values)
152             sub where
153             {
154 0     0 0 0 my $self = shift;
155 0         0 $self->where_obj->append(new DBomb::Query::Expr(@_));
156 0         0 return $self;
157             }
158              
159             ## and (EXPR, @bind_values)
160             sub and
161             {
162 0     0 0 0 my $self = shift;
163 0         0 $self->where_obj->and(@_);
164 0         0 $self
165             }
166              
167             ## or (EXPR, @bind_values)
168             sub or
169             {
170 0     0 0 0 my $self = shift;
171 0         0 $self->where_obj->or(@_);
172 0         0 $self
173             }
174              
175             sub group_by
176             {
177 0     0 0 0 my $self = shift;
178 0         0 my $o = new DBomb::Query::GroupBy(@_);
179 0         0 push @{$self->groupbys}, $o;
  0         0  
180 0         0 return $self;
181             }
182              
183             sub having
184             {
185 0     0 0 0 my $self = shift;
186 0         0 my $o = new DBomb::Query::Expr(@_);
187 0         0 $self->having_obj->append($o);
188 0         0 return $self;
189             }
190              
191             sub order_by
192             {
193 4     4 0 14 my $self = shift;
194 4         114 my $o = new DBomb::Query::OrderBy(@_);
195 4         128 push @{$self->orderbys}, $o;
  4         105  
196 4         42 return $self;
197             }
198              
199             sub asc
200             {
201 0     0 0 0 my $self = shift;
202 0         0 assert(@_ == 0, "asc takes no arguments");
203 0         0 my $orderbys = $self->orderbys;
204 0         0 assert(@$orderbys > 0, "asc requires previous call to order_by");
205 0         0 $orderbys->[$#$orderbys]->asc;
206 0         0 return $self;
207             }
208              
209             sub desc
210             {
211 2     2 0 3 my $self = shift;
212 2         8 assert(@_ == 0, "desc takes no arguments");
213 2         56 my $orderbys = $self->orderbys;
214 2         24 assert(@$orderbys > 0, "asc requires previous call to order_by");
215 2         13 $orderbys->[$#$orderbys]->desc;
216 2         32 return $self;
217             }
218              
219             sub limit
220             {
221 3     3 0 7 my $self = shift;
222 3         94 my $o = new DBomb::Query::Limit(@_);
223 3         191 $self->limit_obj($o);
224 3         37 return $self;
225             }
226              
227             sub sql
228             {
229 0     0 0   my ($self, $dbh) = @_;
230 0 0         $self->dbh($dbh) if defined $dbh;
231 0           $dbh = $self->dbh();
232 0           assert(defined($dbh), 'DBomb::Query::sql method requires a dbh');
233              
234 0           my $sql = "SELECT ";
235              
236 0 0         if ($self->_sql_small_result){
237 0           $sql .= "SQL_SMALL_RESULT ";
238             }
239              
240 0           $sql .= CORE::join ',', @{$self->column_names};
  0            
241              
242 0 0         $sql .= " FROM " . CORE::join ', ', map { $_->sql($dbh) } @{$self->table_sources} if @{$self->table_sources};
  0            
  0            
  0            
243              
244 0           my $where_sql = $self->where_obj->sql($dbh);
245 0 0         $sql .= " WHERE $where_sql " if length $where_sql;
246              
247 0           my $groupby_sql = CORE::join ', ', map { $_->sql($dbh) } @{$self->groupbys};
  0            
  0            
248 0 0         $sql .= " GROUP BY " . $groupby_sql if length $groupby_sql;
249              
250 0           my $having_sql = $self->having_obj->sql($dbh);
251 0 0         $sql .= " HAVING $having_sql " if length $having_sql;
252              
253 0           my $orderby_sql = CORE::join ', ', map { $_->sql($dbh) } @{$self->orderbys};
  0            
  0            
254 0 0         $sql .= " ORDER BY " . $orderby_sql if length $orderby_sql;
255              
256 0 0         $sql .= $self->limit_obj->sql($dbh) if defined $self->limit_obj;
257              
258 0           return ctx_0($sql,@{$self->bind_values});
  0            
259             }
260              
261             sub bind_values
262             {
263 0     0 0   my $self = shift;
264 0           my $bv = [];
265              
266 0           push @$bv, map{ @{$_->bind_values} } @{$self->table_sources};
  0            
  0            
  0            
267 0           push @$bv, @{$self->where_obj->bind_values};
  0            
268 0           push @$bv, @{$self->having_obj->bind_values};
  0            
269 0           return $bv;
270             }
271              
272             ## prepare()
273             ## prepare($dbh)
274             sub prepare
275             {
276 0     0 0   my ($self,$dbh) = @_;
277              
278 0 0         $self->dbh($dbh) if defined $dbh;
279 0           assert(defined($self->dbh), 'prepare requires a dbh');
280              
281 0 0         if ($DBomb::Conf::prepare_cached){
282 0           $self->sth($self->dbh->prepare_cached(scalar $self->sql));
283             }
284             else{
285 0           $self->sth($self->dbh->prepare(scalar $self->sql));
286             }
287 0           return $self;
288             }
289              
290             ## execute(@bind_values)
291             ## execute($dbh,@bind_values)
292             sub execute
293             {
294 0     0 0   my ($self, @bind_values) = @_;
295              
296 0 0         $self->dbh(shift @bind_values) if UNIVERSAL::isa($bind_values[0],'DBI::db');
297 0           assert(defined($self->dbh), 'execute requires a dbh');
298              
299 0 0         $self->prepare unless $self->sth;
300 0           assert($self->sth, 'execute requires a valid sth. Did you forget to call prepare()?');
301              
302 0           $self->sth->execute((@{$self->bind_values},@bind_values));
  0            
303 0           return $self;
304             }
305              
306             ## fetchrow_arrayref()
307             sub fetchrow_arrayref
308             {
309 0     0 0   my $self = shift;
310 0           assert(defined($self->sth), 'fetchrow_arrayref requires an sth');
311              
312 0           return $self->sth->fetchrow_arrayref;
313             }
314              
315             ## fetchall_arrayref()
316             sub fetchall_arrayref
317             {
318 0     0 0   my $self = shift;
319 0           my $a = [];
320 0           while(my $row = $self->fetchrow_arrayref){
321              
322             ## DBI reuses array refs, so create new ones each time
323 0           push @$a, [@$row];
324             }
325 0           return $a;
326             }
327              
328             ## fetchcol_arrayref()
329             sub fetchcol_arrayref
330             {
331 0     0 0   my $self = shift;
332 0           my $a = [];
333 0           while(my $row = $self->fetchrow_arrayref){
334              
335 0           push @$a, $row->[0];
336             }
337 0           return $a;
338             }
339              
340             ## selectall_arrayref(@bind_values)
341             ## selectall_arrayref($dbh, @bind_values)
342             sub selectall_arrayref
343             {
344 0     0 0   my ($self, @bind_values) = @_;
345              
346 0 0         $self->dbh(shift @bind_values) if UNIVERSAL::isa($bind_values[0],'DBI::db');
347 0           assert(defined($self->dbh), 'execute requires a dbh');
348              
349 0 0         $self->prepare($self->dbh) unless $self->sth;
350 0           $self->execute(@bind_values);
351 0           return $self->fetchall_arrayref;
352             }
353              
354             ## selectcol_arrayref(@bind_values)
355             ## selectcol_arrayref($dbh, @bind_values)
356             sub selectcol_arrayref
357             {
358 0     0 0   my ($self, @bind_values) = @_;
359              
360 0 0         $self->dbh(shift @bind_values) if UNIVERSAL::isa($bind_values[0],'DBI::db');
361 0           assert(defined($self->dbh), 'execute requires a dbh');
362              
363 0 0         $self->prepare($self->dbh) unless $self->sth;
364 0           $self->execute(@bind_values);
365 0           return $self->fetchcol_arrayref;
366             }
367              
368             ## finish()
369             sub finish
370             {
371 0     0 0   my $self = shift;
372 0           assert(defined($self->sth), 'finish() requires an sth');
373 0           $self->sth->finish;
374             }
375              
376             ## returns a deep copy.
377             ## @note The database handle will be shared by the clone, and
378             ## the internal statement handle will set to undef in the clone.
379             ## clone ()
380             sub clone
381             {
382 0     0 0   my $self = shift;
383              
384 0           assert(UNIVERSAL::isa($self,__PACKAGE__));
385 0           assert(@_ == 0);
386              
387 0           my $clone = __PACKAGE__->new();
388              
389             ## copy simple lists
390 0 0         push @{$clone->column_names}, @{$self->column_names} if @{$self->column_names};
  0            
  0            
  0            
391 0 0         push @{$clone->table_sources}, @{$self->table_sources} if @{$self->table_sources};
  0            
  0            
  0            
392              
393             ## clone object lists
394 0 0         push @{$clone->orderbys}, map{$_->clone} @{$self->orderbys} if @{$self->orderbys};
  0            
  0            
  0            
  0            
395 0 0         push @{$clone->groupbys}, map{$_->clone} @{$self->groupbys} if @{$self->groupbys};
  0            
  0            
  0            
  0            
396              
397             ## clone objects
398 0 0         $clone->where_obj ($self->where_obj->clone) if $self->where_obj;
399 0 0         $clone->having_obj ($self->having_obj) if $self->having_obj;
400 0 0         $clone->limit_obj ($self->limit_obj->clone) if $self->limit_obj;
401              
402             ## share the dbh, and undef the sth
403 0           $clone->dbh($self->dbh);
404 0           $clone->sth(undef);
405              
406 0           return $clone;
407             }
408              
409             1;
410             __END__