File Coverage

blib/lib/DBIx/ThinSQL.pm
Criterion Covered Total %
statement 360 468 76.9
branch 118 172 68.6
condition 9 26 34.6
subroutine 58 94 61.7
pod 6 25 24.0
total 551 785 70.1


line stmt bran cond sub pod time code
1             package DBIx::ThinSQL;
2 3     3   47801 use strict;
  3         3  
  3         64  
3 3     3   8 use warnings;
  3         3  
  3         54  
4 3     3   3887 use DBI;
  3         35577  
  3         180  
5             use Exporter::Tidy
6 3         19 other => [qw/ bv qv qi sq func OR AND /],
7             sql => [
8             qw/
9             case
10             cast
11             coalesce
12             concat
13             count
14             exists
15             hex
16             length
17             lower
18             ltrim
19             max
20             min
21             replace
22             rtrim
23             substr
24             sum
25             upper
26             /
27 3     3   2533 ];
  3         23  
28              
29             our @ISA = 'DBI';
30             our $VERSION = '0.0.45_1';
31              
32             sub ejoin {
33 0     0 0 0 my $joiner = shift;
34 0 0       0 return unless @_;
35              
36 0         0 my @tokens = map { $_, $joiner } @_;
  0         0  
37 0         0 pop @tokens;
38              
39 0         0 return @tokens;
40             }
41              
42             sub func {
43 0     0 1 0 my $func = uc shift;
44 0         0 my $joiner = shift;
45              
46 0         0 return DBIx::ThinSQL::expr->new( $func, '(',
47             DBIx::ThinSQL::ejoin( $joiner, @_ ), ')' );
48             }
49              
50 7     7 1 67413 sub bv { DBIx::ThinSQL::expr->new( DBIx::ThinSQL::bind_value->new(@_) ) }
51              
52 3     3 1 1174 sub qv { DBIx::ThinSQL::expr->new( DBIx::ThinSQL::quote_value->new(@_) ) }
53              
54 0     0 0 0 sub qi { DBIx::ThinSQL::expr->new( DBIx::ThinSQL::quote_identifier->new(@_) ) }
55              
56 0     0 1 0 sub OR { ' OR ' }
57              
58 0     0 1 0 sub AND { ' AND ' }
59              
60 0     0 0 0 sub cast { func( 'cast', ' ', @_ ) }
61              
62 0     0 0 0 sub case { DBIx::ThinSQL::case->new(@_) }
63              
64 0     0 0 0 sub coalesce { func( 'coalesce', ', ', @_ ) }
65              
66 0     0 0 0 sub concat { DBIx::ThinSQL::expr->new( DBIx::ThinSQL::ejoin( ' || ', @_ ) ) }
67              
68 0     0 0 0 sub count { func( 'count', ', ', @_ ) }
69              
70 0     0 0 0 sub exists { func( 'exists', ', ', @_ ) }
71              
72 0     0 0 0 sub hex { func( 'hex', ', ', @_ ) }
73              
74 0     0 0 0 sub length { func( 'length', ', ', @_ ) }
75              
76 0     0 0 0 sub lower { func( 'lower', ', ', @_ ) }
77              
78 0     0 0 0 sub ltrim { func( 'ltrim', ', ', @_ ) }
79              
80 0     0 0 0 sub max { func( 'max', ', ', @_ ) }
81              
82 0     0 0 0 sub min { func( 'min', ', ', @_ ) }
83              
84 0     0 0 0 sub replace { func( 'replace', ', ', @_ ) }
85              
86 0     0 0 0 sub rtrim { func( 'rtrim', ', ', @_ ) }
87              
88 0     0 0 0 sub substr { func( 'substr', ', ', @_ ) }
89              
90 1     1 1 407 sub sq { DBIx::ThinSQL::query->new(@_) }
91              
92 0     0 0 0 sub sum { func( 'sum', '', @_ ) }
93              
94 0     0 0 0 sub upper { func( 'upper', ', ', @_ ) }
95              
96             package DBIx::ThinSQL::db;
97 3     3   1429 use strict;
  3         4  
  3         58  
98 3     3   11 use warnings;
  3         2  
  3         69  
99 3     3   9 use Carp ();
  3         3  
  3         38  
100 3     3   1216 use Log::Any '$log';
  3         30955  
  3         10  
101 3     3   9712 use DBIx::ThinSQL::Driver;
  3         6  
  3         4698  
102              
103             our @ISA = qw(DBI::db);
104             our @CARP_NOT;
105              
106             sub share_dir {
107 35     35   251 require Path::Tiny;
108              
109 35 50       102 return Path::Tiny::path($DBIX::ThinSQL::SHARE_DIR)
110             if defined $DBIX::ThinSQL::SHARE_DIR;
111              
112 35         97 require File::ShareDir;
113 35         134 return Path::Tiny::path( File::ShareDir::dist_dir('DBIx-ThinSQL') );
114             }
115              
116             sub throw_error {
117 2     2   3 my $self = shift;
118 2         385 Carp::croak(@_);
119             }
120              
121             sub sql_bv {
122 225     225   157 my $self = shift;
123 225         140 my $sql = shift;
124 225         140 my $bv = shift;
125 225         129 my $val = shift;
126 225         154 my $prefix = shift;
127              
128 225 100       272 $prefix = '' unless length($prefix);
129              
130 225         188 my $ref = ref $val;
131 225         186 my $prefix2 = $prefix . ' ';
132              
133             # When we call ourself we already have a ref
134              
135 225 100       480 if ( $ref eq '' ) {
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    100          
    50          
136 110 50       191 $$sql .= defined $val ? $val : 'NULL';
137             }
138             elsif ( $ref eq 'DBIx::ThinSQL::query' ) {
139 34 100       59 my $bracket = length($prefix) ? '(' : '';
140 34         58 foreach my $pair ( $val->tokens ) {
141 89 50       157 $$sql .= "\n" if $pair->[0] =~ /UNION/;
142 89 50 33     441 my $join_on = length( $pair->[0] )
143             && ( $pair->[0] =~ m/(JOIN)|(ON)/ ) ? ' ' : '';
144 89 50 66     339 $$sql .=
145             ( $bracket || $prefix . $join_on ) . $pair->[0] . "\n" . $prefix2
146             if length( $pair->[0] );
147 89         140 $self->sql_bv( $sql, $bv, $pair->[1], $prefix2 );
148 89 50 33     249 $$sql .= "\n" if length( $pair->[0] ) or length( $pair->[1] );
149 89         100 $bracket = '';
150             }
151 34 100       83 $$sql .= $prefix . ")" if length($prefix);
152             }
153             elsif ( $ref eq 'DBIx::ThinSQL::list' ) {
154 21         33 my @tokens = $val->tokens;
155 21         28 my $last = pop @tokens;
156 21         24 my $i = 0;
157 21         24 foreach my $token (@tokens) {
158 19 50       31 $$sql .= $prefix if $i++;
159 19         26 $self->sql_bv( $sql, $bv, $token, $prefix );
160 19         35 $$sql .= ",\n";
161             }
162 21         23 $$sql .= $prefix;
163 21         31 $self->sql_bv( $sql, $bv, $last, $prefix );
164             }
165             elsif ( $ref eq 'DBIx::ThinSQL::table' ) {
166 4         15 my @tokens = $val->tokens;
167 4         8 my $table = shift @tokens;
168 4         8 $$sql .= $table . "(\n";
169              
170 4         7 my $last = pop @tokens;
171 4         6 foreach my $token (@tokens) {
172 4         5 $$sql .= $prefix2;
173 4         8 $self->sql_bv( $sql, $bv, $token, $prefix2 );
174 4         7 $$sql .= ",\n";
175             }
176 4         3 $$sql .= $prefix2;
177 4         8 $self->sql_bv( $sql, $bv, $last, $prefix2 );
178 4         10 $$sql .= "\n" . $prefix . ")";
179             }
180             elsif ( $ref eq 'DBIx::ThinSQL::values' ) {
181 8         19 my @tokens = $val->tokens;
182 8         12 $$sql .= "(\n";
183              
184 8         6 my $last = pop @tokens;
185 8         14 foreach my $token (@tokens) {
186 8         5 $$sql .= $prefix2;
187 8         21 $self->sql_bv( $sql, $bv, $token, $prefix2 );
188 8         10 $$sql .= ",\n";
189             }
190 8         9 $$sql .= $prefix2;
191 8         12 $self->sql_bv( $sql, $bv, $last, $prefix2 );
192 8         15 $$sql .= "\n" . $prefix . ")";
193             }
194             elsif ( $ref eq 'DBIx::ThinSQL::case' ) {
195 0         0 $$sql .= "CASE\n";
196 0         0 my @tokens = $val->tokens;
197 0         0 foreach my $pair (@$val) {
198 0         0 $$sql .= $prefix2 . $pair->[0] . "\n" . $prefix2 . ' ';
199 0         0 $self->sql_bv( $sql, $bv, $pair->[1], $prefix2 );
200 0         0 $$sql .= "\n";
201             }
202 0         0 $$sql .= $prefix . "END";
203             }
204             elsif ( $ref eq 'DBIx::ThinSQL::expr' ) {
205 19         33 foreach my $token ( $val->tokens ) {
206 39         51 $self->sql_bv( $sql, $bv, $token, $prefix );
207             }
208             }
209             elsif ( $ref eq 'DBIx::ThinSQL::bind_value' ) {
210 25         16 $$sql .= '?';
211 25         19 push( @{$bv}, $val );
  25         47  
212             }
213             elsif ( $ref eq 'DBIx::ThinSQL::quote_value' ) {
214 3         8 $$sql .= $self->quote( $val->for_quote );
215             }
216             elsif ( $ref eq 'DBIx::ThinSQL::quote_identifier' ) {
217 1         3 $$sql .= $self->quote_identifier( $val->val );
218             }
219             else {
220 0         0 Carp::cluck "sql_bv doesn't know $ref";
221             }
222             }
223              
224             sub query {
225 33     33   36 my $self = shift;
226 33         52 my ( $sql, @bv ) = ('');
227 33         103 $self->sql_bv( \$sql, \@bv, DBIx::ThinSQL::query->new(@_) );
228 33         159 return $sql . ";\n", @bv;
229             }
230              
231             sub xprepare {
232 20     20   20 my $self = shift;
233 20 50       44 Carp::croak('xprepare requires arguments!') unless @_;
234              
235 20         35 my ( $sql, @bv ) = $self->query(@_);
236              
237             # TODO these locals have no effect?
238 20         135 local $self->{RaiseError} = 1;
239 20         281 local $self->{PrintError} = 0;
240 20         160 local $self->{ShowErrorStatement} = 1;
241              
242 20         131 my $prepare_ok;
243 20         22 my $sth = eval {
244 20         67 my $sth = $self->prepare($sql);
245 20         1344 $prepare_ok = 1;
246              
247 20         22 my $i = 1;
248 20         38 foreach my $bv (@bv) {
249 2         11 $sth->bind_param( $i++, $bv->for_bind_param );
250             }
251              
252 20         23 $sth;
253             };
254              
255 20 50       39 if ($@) {
256 0 0       0 $log->debug($sql) unless $prepare_ok;
257 0         0 $self->throw_error($@);
258             }
259              
260 20         203 return $sth;
261             }
262              
263             sub xdo {
264 13     13   1919 my $self = shift;
265 13         39 my ( $sql, @bv ) = $self->query(@_);
266              
267             # TODO these locals have no effect?
268 13         116 local $self->{RaiseError} = 1;
269 13         249 local $self->{PrintError} = 0;
270 13         109 local $self->{ShowErrorStatement} = 1;
271              
272 13 100       106 return $self->do($sql) unless @bv;
273              
274 12         14 my $sth = eval {
275 12         68 my $sth = $self->prepare($sql);
276 12         954 my $i = 1;
277 12         35 foreach my $bv (@bv) {
278 23         58 $sth->bind_param( $i++, $bv->for_bind_param );
279             }
280              
281 12         20 $sth;
282             };
283              
284 12 50       46 $self->throw_error($@) if $@;
285              
286 12         78358 return $sth->execute;
287             }
288              
289             sub log_debug {
290 0     0   0 my $self = shift;
291 0         0 my $sql = (shift) . "\n";
292              
293 0         0 my $sth = $self->prepare( $sql . ';' );
294 0         0 $sth->execute(@_);
295              
296 0         0 my $out = join( ', ', @{ $sth->{NAME} } ) . "\n";
  0         0  
297 0         0 $out .= ' ' . ( '-' x length $out ) . "\n";
298 0         0 $out .= ' ' . DBI::neat_list($_) . "\n" for @{ $sth->fetchall_arrayref };
  0         0  
299 0         0 $log->debug($out);
300             }
301              
302             sub log_warn {
303 0     0   0 my $self = shift;
304 0         0 my $sql = (shift) . "\n";
305              
306 0         0 my $sth = $self->prepare( $sql . ';' );
307 0         0 $sth->execute(@_);
308              
309 0         0 my $out = join( ', ', @{ $sth->{NAME} } ) . "\n";
  0         0  
310 0         0 $out .= ' ' . ( '-' x length $out ) . "\n";
311 0         0 $out .= ' ' . DBI::neat_list($_) . "\n" for @{ $sth->fetchall_arrayref };
  0         0  
312 0         0 warn $out;
313             }
314              
315             sub dump {
316 0     0   0 my $self = shift;
317 0         0 my $sth = $self->prepare(shift);
318 0         0 $sth->execute(@_);
319 0         0 $sth->dump_results;
320             }
321              
322             sub xdump {
323 0     0   0 my $self = shift;
324 0         0 my $sth = $self->xprepare(@_);
325 0         0 $sth->execute;
326 0         0 $sth->dump_results;
327             }
328              
329             sub xval {
330 3     3   792 my $self = shift;
331              
332 3         11 my $sth = $self->xprepare(@_);
333 3         133 $sth->execute;
334 3         12 my $ref = $sth->arrayref;
335 3         20 $sth->finish;
336              
337 3 50       37 return $ref->[0] if $ref;
338 0         0 return;
339             }
340              
341             sub xvals {
342 0     0   0 my $self = shift;
343 0         0 my $sth = $self->xprepare(@_);
344 0         0 $sth->execute;
345 0         0 return $sth->vals;
346             }
347              
348             sub xlist {
349 2     2   2342 my $self = shift;
350              
351 2         5 my $sth = $self->xprepare(@_);
352 2         118 $sth->execute;
353 2         6 my $ref = $sth->arrayref;
354 2         15 $sth->finish;
355              
356 2 100       30 return @$ref if $ref;
357 1         12 return;
358             }
359              
360             sub xarrayref {
361 2     2   2017 my $self = shift;
362              
363 2         6 my $sth = $self->xprepare(@_);
364 2         82 $sth->execute;
365 2         5 my $ref = $sth->arrayref;
366 2         11 $sth->finish;
367              
368 2 100       15 return $ref if $ref;
369 1         10 return;
370             }
371              
372             sub xarrayrefs {
373 7     7   3185 my $self = shift;
374              
375 7         16 my $sth = $self->xprepare(@_);
376 7         259 $sth->execute;
377              
378 7         21 return $sth->arrayrefs;
379             }
380              
381             sub xhashref {
382 2     2   1970 my $self = shift;
383              
384 2         5 my $sth = $self->xprepare(@_);
385 2         82 $sth->execute;
386 2         7 my $ref = $sth->hashref;
387 2         14 $sth->finish;
388              
389 2 100       14 return $ref if $ref;
390 1         11 return;
391             }
392              
393             sub xhashrefs {
394 4     4   3444 my $self = shift;
395              
396 4         10 my $sth = $self->xprepare(@_);
397 4         164 $sth->execute;
398 4         11 return $sth->hashrefs;
399             }
400              
401             # Can't use 'local' to managed txn count here because $self is a tied hashref?
402             # Also can't use ||=.
403             sub txn {
404 6     6   7508 my $self = shift;
405 6         7 my $subref = shift;
406 6         7 my $wantarray = wantarray;
407 6         64 my $txn = $self->{private_DBIx_ThinSQL_txn}++;
408 6         22 my $driver = $self->{private_DBIx_ThinSQL_driver};
409              
410 6   66     17 $driver ||= $self->{private_DBIx_ThinSQL_driver} = do {
411 1         15 my $class = 'DBIx::ThinSQL::Driver::' . $self->{Driver}->{Name};
412 1         5 ( my $path = $class ) =~ s{::}{/}g;
413 1         2 $path .= '.pm';
414              
415 1 50       1 eval { require $path; $class->new } || DBIx::ThinSQL::Driver->new;
  1         642  
  1         10  
416             };
417              
418 6         7 my $current;
419 6 100       13 if ( !$txn ) {
420             $current = {
421             RaiseError => $self->{RaiseError},
422             ShowErrorStatement => $self->{ShowErrorStatement},
423 4         28 };
424              
425             }
426              
427 6 50       25 $self->{RaiseError} = 1 unless exists $self->{HandleError};
428 6         64 $self->{ShowErrorStatement} = 1;
429              
430 6         9 my @result;
431             my $result;
432              
433 6 100       14 if ( !$txn ) {
434 4         31 $self->begin_work;
435             }
436             else {
437 2         13 $driver->savepoint( $self, 'txn' . $txn );
438             }
439              
440 6         122 eval {
441              
442 6 100       9 if ($wantarray) {
443 1         3 @result = $subref->();
444             }
445             else {
446 5         9 $result = $subref->();
447             }
448              
449 4 100       271 if ( !$txn ) {
450              
451             # We check again for the AutoCommit state in case the
452             # $subref did something like its own ->rollback(). This
453             # really just prevents a warning from being printed.
454 3 50       3227 $self->commit unless $self->{AutoCommit};
455             }
456             else {
457             $driver->release( $self, 'txn' . $txn )
458 1 50       11 unless $self->{AutoCommit};
459             }
460              
461             };
462 6         8332 my $error = $@;
463              
464 6         75 $self->{private_DBIx_ThinSQL_txn} = $txn;
465 6 100       21 if ( !$txn ) {
466 4         18 $self->{RaiseError} = $current->{RaiseError};
467 4         18 $self->{ShowErrorStatement} = $current->{ShowErrorStatement};
468             }
469              
470 6 100       17 if ($error) {
471              
472 2         3 eval {
473 2 100       6 if ( !$txn ) {
474              
475             # If the transaction failed at COMMIT, then we can no
476             # longer roll back. Maybe put this around the eval for
477             # the RELEASE case as well??
478 1 50       12 $self->rollback unless $self->{AutoCommit};
479             }
480             else {
481             $driver->rollback_to( $self, 'txn' . $txn )
482 1 50       13 unless $self->{AutoCommit};
483             }
484             };
485              
486 2 50       39 $self->throw_error(
487             $error . "\nAdditionally, an error occured during
488             rollback:\n$@"
489             ) if $@;
490              
491 2         9 $self->throw_error($error);
492             }
493              
494 4 100       32 return $wantarray ? @result : $result;
495             }
496              
497             package DBIx::ThinSQL::st;
498 3     3   15 use strict;
  3         4  
  3         47  
499 3     3   7 use warnings;
  3         4  
  3         809  
500              
501             our @ISA = qw(DBI::st);
502              
503             sub val {
504 0     0   0 my $self = shift;
505 0   0     0 my $ref = $self->fetchrow_arrayref || return;
506 0         0 return $ref->[0];
507             }
508              
509             sub vals {
510 0     0   0 my $self = shift;
511 0   0     0 my $all = $self->fetchall_arrayref || return;
512 0 0       0 return unless @$all;
513 0 0       0 return map { $_->[0] } @$all if wantarray;
  0         0  
514 0         0 return [ map { $_->[0] } @$all ];
  0         0  
515             }
516              
517             sub list {
518 0     0   0 my $self = shift;
519 0   0     0 my $ref = $self->fetchrow_arrayref || return;
520 0         0 return @$ref;
521             }
522              
523             sub arrayref {
524 7     7   9 my $self = shift;
525 7 50       77 return unless $self->{Active};
526 7         67 return $self->fetchrow_arrayref;
527             }
528              
529             sub arrayrefs {
530 7     7   8 my $self = shift;
531 7 50       50 return unless $self->{Active};
532              
533 7   50     96 my $all = $self->fetchall_arrayref || return;
534 7 100       36 return unless @$all;
535 5 100       33 return @$all if wantarray;
536 3         32 return $all;
537             }
538              
539             sub hashref {
540 2     2   2 my $self = shift;
541 2 50       14 return unless $self->{Active};
542              
543 2         42 return $self->fetchrow_hashref('NAME_lc');
544             }
545              
546             sub hashrefs {
547 4     4   5 my $self = shift;
548 4 50       52 return unless $self->{Active};
549              
550 4         6 my @all;
551 4         60 while ( my $ref = $self->fetchrow_hashref('NAME_lc') ) {
552 4         43 push( @all, $ref );
553             }
554              
555 4 100       30 return @all if wantarray;
556 2         21 return \@all;
557             }
558              
559             package DBIx::ThinSQL::bind_value;
560 3     3   10 use strict;
  3         7  
  3         53  
561 3     3   9 use warnings;
  3         2  
  3         481  
562             our @ISA = ('DBIx::ThinSQL::expr');
563              
564             sub new {
565 27     27   30 my $class = shift;
566 27 100       87 return $_[0] if ref( $_[0] ) =~ m/DBIx::ThinSQL/;
567 25 50       48 return $$_[0] if ref $_[0] eq 'SCALAR';
568 25         97 return bless [@_], $class;
569             }
570              
571             sub val {
572 0     0   0 return $_[0]->[0];
573             }
574              
575             sub type {
576 0     0   0 return $_[0]->[1];
577             }
578              
579             sub for_bind_param {
580 25     25   29 my $self = shift;
581              
582             # value, type
583 25 50       55 return @$self if defined $self->[1];
584              
585             # value
586 25         123 return $self->[0];
587             }
588              
589             package DBIx::ThinSQL::quote_value;
590 3     3   11 use strict;
  3         2  
  3         47  
591 3     3   8 use warnings;
  3         2  
  3         413  
592             our @ISA = ('DBIx::ThinSQL::expr');
593              
594             sub new {
595 3     3   77 my $class = shift;
596 3 50       9 return $_[0] if ref( $_[0] ) =~ m/DBIx::ThinSQL/;
597 3 50       8 return $$_[0] if ref $_[0] eq 'SCALAR';
598 3         13 return bless [@_], $class;
599             }
600              
601             sub val {
602 0     0   0 return $_[0]->[0];
603             }
604              
605             sub type {
606 0     0   0 return $_[0]->[1];
607             }
608              
609             sub for_quote {
610 3     3   3 my $self = shift;
611              
612             # value, type
613 3 50       12 return @$self if defined $self->[1];
614              
615             # value
616 3         53 return $self->[0];
617             }
618              
619             package DBIx::ThinSQL::quote_identifier;
620 3     3   9 use strict;
  3         6  
  3         43  
621 3     3   8 use warnings;
  3         2  
  3         178  
622              
623             sub new {
624 1     1   2 my $class = shift;
625 1         1 my $id = shift;
626 1         4 return bless \$id, $class;
627             }
628              
629             sub val {
630 1     1   2 my $self = shift;
631 1         20 return $$self;
632             }
633              
634             package DBIx::ThinSQL::expr;
635 3     3   9 use strict;
  3         3  
  3         42  
636 3     3   7 use warnings;
  3         2  
  3         1222  
637              
638             sub new {
639 19     19   18 my $class = shift;
640 19         22 my @tokens;
641 19         29 foreach my $token (@_) {
642 31 50       69 if ( ref $token eq 'ARRAY' ) {
    100          
643 0         0 push( @tokens, DBIx::ThinSQL::query->new(@$token) );
644             }
645             elsif ( ref $token eq 'HASH' ) {
646 2         12 my @cols = sort keys %$token;
647 2         2 my $narg;
648              
649 2         5 foreach my $col (@cols) {
650 2 50       14 if ( ref $token->{$col} eq 'SCALAR' ) {
    100          
    50          
651 0         0 $narg->{$col} = ${ $token->{$col} };
  0         0  
652             }
653             elsif ( ref $token->{$col} eq 'ARRAY' ) {
654             $narg->{$col} =
655 2         4 [ map { DBIx::ThinSQL::bind_value->new($_) }
656 1         2 @{ $token->{$col} } ];
  1         4  
657             }
658             elsif ( defined $token->{$col} ) {
659             $narg->{$col} =
660 1         4 DBIx::ThinSQL::bind_value->new( $token->{$col} );
661             }
662             }
663              
664 2         5 foreach my $col (@cols) {
665 2         33 my $val = $narg->{$col};
666 2         83 print "$col $val\n";
667              
668 2         10 my $like = $col =~ s/\s+like$/ LIKE /i;
669 2         3 my $not_like = $col =~ s/\s+(!|not)\s*like$/ NOT LIKE /i;
670 2         12 my $not = $col =~ s/\s*!$//;
671 2         6 my $gtlt = $col =~ s/(\s+[><]=?)$/$1 /;
672              
673 2         4 push( @tokens, $col );
674 2 50       9 if ( !defined $val ) {
    100          
675 0 0       0 push( @tokens, ' IS ', $not ? 'NOT NULL' : 'NULL' );
676             }
677             elsif ( ref $val eq 'ARRAY' ) {
678 1 50       4 push( @tokens, ' NOT' ) if $not;
679 1         2 push( @tokens, ' IN (', map { $_, ',' } @$val );
  2         11  
680 1 50       3 pop(@tokens) if @$val;
681 1         1 push( @tokens, ')' );
682             }
683             else {
684 1 50 33     11 push( @tokens, $not ? ' != ' : ' = ' )
    50 33        
685             unless $like
686             or $not_like
687             or $gtlt;
688              
689 1         2 push( @tokens, $val );
690             }
691 2         6 push( @tokens, ' AND ' );
692             }
693 2         6 pop @tokens;
694             }
695             else {
696 29         40 push( @tokens, $token );
697             }
698             }
699 19         108 return bless \@tokens, $class;
700             }
701              
702             sub as {
703 0     0   0 my $self = shift;
704 0         0 my $value = shift;
705              
706 0         0 return DBIx::ThinSQL::expr->new( $self, ' AS ',
707             DBIx::ThinSQL::quote_identifier->new($value) );
708             }
709              
710             sub tokens {
711 19     19   17 my $self = shift;
712 19         30 return @$self;
713             }
714              
715             package DBIx::ThinSQL::case;
716 3     3   13 use strict;
  3         3  
  3         50  
717 3     3   8 use warnings;
  3         2  
  3         386  
718             our @ISA = ('DBIx::ThinSQL::expr');
719              
720             sub new {
721 0     0   0 my $class = shift;
722 0         0 my @tokens;
723              
724 0         0 while ( my ( $key, $val ) = splice( @_, 0, 2 ) ) {
725 0         0 ( $key = uc($key) ) =~ s/_/ /g;
726 0         0 push( @tokens, [ $key, DBIx::ThinSQL::expr->new($val) ] );
727             }
728              
729 0         0 return bless \@tokens, $class;
730             }
731              
732             package DBIx::ThinSQL::list;
733 3     3   13 use strict;
  3         3  
  3         53  
734 3     3   9 use warnings;
  3         3  
  3         364  
735              
736             sub new {
737 33     33   35 my $class = shift;
738 33         29 my @tokens;
739 33         54 foreach my $token (@_) {
740 68 50       150 if ( ref $token eq 'ARRAY' ) {
741 0         0 push( @tokens, DBIx::ThinSQL::query->new(@$token) );
742             }
743             else {
744 68         95 push( @tokens, $token );
745             }
746             }
747 33         138 return bless \@tokens, $class;
748             }
749              
750             sub tokens {
751 33     33   35 my $self = shift;
752 33         89 return @$self;
753             }
754              
755             package DBIx::ThinSQL::table;
756             our @ISA = ('DBIx::ThinSQL::list');
757              
758             package DBIx::ThinSQL::values;
759             our @ISA = ('DBIx::ThinSQL::list');
760              
761             package DBIx::ThinSQL::query;
762 3     3   9 use strict;
  3         2  
  3         43  
763 3     3   7 use warnings;
  3         2  
  3         1737  
764              
765             sub new {
766 34     34   33 my $class = shift;
767 34         29 my @query;
768              
769 34         42 eval {
770 34         114 while ( my ( $word, $arg ) = splice( @_, 0, 2 ) ) {
771 89         240 ( $word = uc($word) ) =~ s/_/ /g;
772 89         86 my $ref = ref $arg;
773              
774 89 100       194 if ( $ref =~ m/^DBIx::ThinSQL/ ) {
    100          
    100          
775 1         5 push( @query, [ $word, $arg ] );
776             }
777             elsif ( $ref eq 'ARRAY' ) {
778 29 100       143 if ( $word =~ m/((SELECT)|(ORDER)|(GROUP))/ ) {
    50          
    50          
    100          
779 20         53 push( @query, [ $word, DBIx::ThinSQL::list->new(@$arg) ] );
780             }
781             elsif ( $word =~ m/INSERT/ ) {
782 0         0 push( @query, [ $word, DBIx::ThinSQL::table->new(@$arg) ] );
783             }
784             elsif ( $word =~ m/(^AS$)|(FROM)|(JOIN)/ ) {
785 0         0 push( @query, [ $word, DBIx::ThinSQL::query->new(@$arg) ] );
786             }
787             elsif ( $word eq 'VALUES' ) {
788             push(
789             @query,
790             [
791             $word,
792             DBIx::ThinSQL::values->new(
793 4         8 map { DBIx::ThinSQL::bind_value->new($_) }
  8         16  
794             @$arg
795             )
796             ]
797             );
798             }
799             else {
800 5         12 push( @query, [ $word, DBIx::ThinSQL::expr->new(@$arg) ] );
801             }
802             }
803             elsif ( $ref eq 'HASH' ) {
804              
805 7 100       51 if ( $word =~ m/^((WHERE)|(ON))/ ) {
    100          
    50          
806 2         10 push( @query, [ $word, DBIx::ThinSQL::expr->new($arg) ] );
807             }
808             elsif ( $word eq 'SET' ) {
809 1         11 my @cols = sort keys %$arg;
810             push(
811             @query,
812             [
813             $word,
814             DBIx::ThinSQL::list->new(
815             map {
816 1         6 DBIx::ThinSQL::expr->new(
817              
818             # quote_identifier?
819             $_, ' = ',
820             ref $arg->{$_} eq 'SCALAR'
821 0         0 ? ${ $arg->{$_} }
822             : DBIx::ThinSQL::bind_value->new(
823 1 50       14 $arg->{$_}
824             )
825             )
826             } @cols
827             )
828             ]
829             );
830             }
831             elsif ( $word eq 'VALUES' ) {
832 4         24 my @cols = sort keys %$arg;
833              
834             # map quote_identifier?
835 4         25 $query[-1]->[1] =
836             DBIx::ThinSQL::table->new( $query[-1]->[1], @cols );
837              
838             push(
839             @query,
840             [
841             $word,
842             DBIx::ThinSQL::values->new(
843             map {
844 4         10 ref $arg->{$_} eq 'SCALAR'
845 0         0 ? ${ $arg->{$_} }
846             : DBIx::ThinSQL::bind_value->new(
847 8 50       29 $arg->{$_} )
848             } @cols
849             )
850             ]
851             );
852             }
853             else {
854 0         0 warn "cannot handle $word => HASH";
855             }
856             }
857             else {
858 52         186 push( @query, [ $word, $arg ] );
859             }
860             }
861             };
862              
863 34 50       60 Carp::croak("Bad Query: $@") if $@;
864 34         82 return bless \@query, $class;
865             }
866              
867             sub as {
868 1     1   2 my $self = shift;
869 1         3 my $value = shift;
870              
871 1         7 return DBIx::ThinSQL::expr->new( '(', $self, ') AS ',
872             DBIx::ThinSQL::quote_identifier->new($value) );
873             }
874              
875             sub tokens {
876 34     34   29 my $self = shift;
877 34         73 return @$self;
878             }
879              
880             1;
881              
882             __END__