File Coverage

blib/lib/DBIx/ThinSQL.pm
Criterion Covered Total %
statement 350 467 74.9
branch 118 172 68.6
condition 9 26 34.6
subroutine 58 95 61.0
pod 6 25 24.0
total 541 785 68.9


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