File Coverage

blib/lib/Class/DBI/Sweet.pm
Criterion Covered Total %
statement 254 399 63.6
branch 64 164 39.0
condition 42 114 36.8
subroutine 31 37 83.7
pod 12 12 100.0
total 403 726 55.5


line stmt bran cond sub pod time code
1             package Class::DBI::Sweet;
2              
3 57     57   2791743 use strict;
  57         228  
  57         4382  
4 57     57   332 use base 'Class::DBI';
  57         118  
  57         124830  
5 57     57   6782427 use Class::DBI::Iterator; # For the resultset cache
  57         55041  
  57         1862  
6              
7 57     57   78357 use Data::Page;
  57         263912  
  57         1622  
8 57     57   3076 use DBI;
  57         132  
  57         2770  
9 57     57   380 use List::Util;
  57         117  
  57         3903  
10 57     57   335 use Carp qw/croak/;
  57         120  
  57         5591  
11              
12             BEGIN { # Use Time::HiRes' time() if possible
13 57     57   4056 eval "use Time::HiRes";
  57     57   77502  
  57         160013  
  57         475  
14 57 50       6964 unless ($@) {
15 57         329 import Time::HiRes qw/time/;
16             }
17             }
18              
19             if ( $^O eq 'MSWin32' ) {
20             eval "require Win32API::GUID;";
21             }
22             else {
23             eval "require Data::UUID;";
24             }
25              
26             our $UUID_Is_Available = ( $@ ? 0 : 1 );
27              
28             our $VERSION = '0.11';
29              
30             #----------------------------------------------------------------------
31             # RETRIEVING
32             #----------------------------------------------------------------------
33              
34             __PACKAGE__->data_type(
35             __ROWS => DBI::SQL_INTEGER,
36             __OFFSET => DBI::SQL_INTEGER
37             );
38              
39             __PACKAGE__->set_sql( Join_Retrieve_Count => <<'SQL' );
40             SELECT COUNT(*)
41             FROM %s
42             WHERE %s
43             SQL
44              
45             __PACKAGE__->set_sql( Join_Retrieve => <<'SQL' );
46             SELECT __ESSENTIAL(me)__%s
47             FROM %s
48             WHERE %s
49             SQL
50              
51             __PACKAGE__->mk_classdata( default_search_attributes => {} );
52             __PACKAGE__->mk_classdata( profiling_data => {} );
53             __PACKAGE__->mk_classdata( _live_resultset_cache => {} );
54              
55             sub retrieve_next {
56 3     3 1 2345 my $self = shift;
57 3   33     51 my $class = ref $self
58             || croak("retrieve_next cannot be called as a class method");
59              
60 3         13 my ( $criteria, $attributes ) = $class->_search_args(@_);
61 3         6 $attributes = { %{$attributes} }; # Local copy to fiddle with
  3         12  
62              
63 3   33     13 my $o_by = $attributes->{order_by} || ( $self->columns('Primary') )[0];
64 3         8 my $is_desc = $o_by =~ s/ +DESC//; # If it's previous we'll add it back
65              
66 3 100       25 my $o_val = (
67             $o_by =~ m/(.*)\.(.*)/
68             ? $self->$1->$2
69             : $self->$o_by
70             );
71              
72 3 50       840 $criteria->{$o_by} = { ( $is_desc ? '<' : '>' ) => $o_val };
73              
74 3   50     28 $attributes->{rows} ||= 1;
75              
76             return wantarray()
77 3 50       9 ? @{ [ $class->_do_search( $criteria, $attributes ) ] }
  3         12  
78             : $class->_do_search( $criteria, $attributes );
79             }
80              
81             sub retrieve_previous {
82 1     1 1 968 my $self = shift;
83 1   33     7 my $class = ref $self
84             || croak("retrieve_previous cannot be called as a class method");
85              
86 1         5 my ( $criteria, $attributes ) = $class->_search_args(@_);
87 1         3 $attributes = { %{$attributes} }; # Local copy to fiddle with
  1         2  
88              
89 1   33     12 my $o_by = $attributes->{order_by} || ( $self->columns('Primary') )[0];
90 1         40 my $is_desc = $o_by =~ s/ +DESC//; # If it's previous we'll add it back
91              
92 1 50       10 my $o_val = (
93             $o_by =~ m/(.*)\.(.*)/
94             ? $self->$1->$2
95             : $self->$o_by
96             );
97              
98 1 50       74 $criteria->{$o_by} = { ( $is_desc ? '>' : '<' ) => $o_val };
99              
100 1 50       13 $attributes->{order_by} = ${o_by} . ( $is_desc ? "" : " DESC" );
101 1   50     22 $attributes->{rows} ||= 1;
102              
103             return wantarray()
104 1 50       4 ? @{ [ $class->_do_search( $criteria, $attributes ) ] }
  1         4  
105             : $class->_do_search( $criteria, $attributes );
106             }
107              
108             sub count {
109 6     6 1 4729753 my $proto = shift;
110 6   33     56 my $class = ref($proto) || $proto;
111              
112 6 50       52 unless (@_) {
113 0         0 return $class->count_all;
114             }
115              
116 6         35 my ( $criteria, $attributes ) = $class->_search_args(@_);
117              
118             # make sure we take copy of $attribues since it can be reused
119 6         13 my $count = { %{$attributes} };
  6         26  
120              
121             # no need for LIMIT/OFFSET and ORDER BY in COUNT(*)
122 6         13 delete @{$count}{qw( rows offset order_by )};
  6         21  
123              
124 6         40 my ( $sql_parts, $classes, $columns, $values ) =
125             $proto->_search( $criteria, $count );
126              
127 6   50     114 my $sql_method = 'sql_' . ( $attributes->{sql_method} || 'Join_Retrieve' );
128 6         14 $sql_method .= '_Count';
129              
130 6         11 my $sth = $class->$sql_method( @{$sql_parts}{qw/ from where /} );
  6         58  
131              
132 6         9981 $class->_bind_param( $sth, $columns );
133              
134 6         168 return $sth->select_val(@$values);
135             }
136              
137             *pager = \&page;
138              
139             sub page {
140 3     3 1 6419596 my $proto = shift;
141 3   33     34 my $class = ref($proto) || $proto;
142              
143 3         69 my ( $criteria, $attributes ) = $proto->_search_args(@_);
144              
145 3   50     14 $attributes->{rows} ||= 10;
146 3   100     16 $attributes->{page} ||= 1;
147 3         10 $attributes->{_pager} = ''; # Flag that we need a pager. How ugly!
148              
149             # No point doing a count(*) if fetching all anyway
150 3 100       14 unless ( $attributes->{disable_sql_paging} ) {
151              
152 2         30 my $page = Data::Page->new( $class->count( $criteria, $attributes ),
153             $attributes->{rows}, $attributes->{page}, );
154              
155 2         2935 $attributes->{offset} = $page->skipped;
156 2         160 $attributes->{_pager} = $page;
157              
158             }
159              
160 3         22 my $iterator = $class->search( $criteria, $attributes );
161              
162 3         19 return ( $attributes->{_pager}, $iterator );
163             }
164              
165             sub retrieve_all {
166 0     0 1 0 my $proto = shift;
167 0   0     0 my $class = ref($proto) || $proto;
168              
169 0 0 0     0 unless ( @_ || keys %{ $class->default_search_attributes } ) {
  0         0  
170 0         0 return $class->SUPER::retrieve_all;
171             }
172              
173 0 0 0     0 return $class->search( {}, ( @_ > 1 ) ? {@_} : ( shift || () ) );
174             }
175              
176             sub search {
177 10     10 1 1905652 my $proto = shift;
178 10   33     76 my $class = ref($proto) || $proto;
179              
180 10         110 my ( $criteria, $attributes ) = $class->_search_args(@_);
181              
182 10         65 $class->_do_search( $criteria, $attributes );
183             }
184              
185             sub search_like {
186 1     1 1 1102 my $proto = shift;
187 1   33     9 my $class = ref($proto) || $proto;
188              
189 1         6 my ( $criteria, $attributes ) = $class->_search_args(@_);
190              
191 1         3 $attributes->{cmp} = 'like';
192              
193 1         5 $class->_do_search( $criteria, $attributes );
194             }
195              
196             sub _do_search {
197 15     15   28 my ( $class, $criteria, $attributes ) = @_;
198              
199 15 50       21 foreach my $pre ( @{ $attributes->{prefetch} || [] } ) {
  15         91  
200 0 0 0     0 unless ( $class->meta_info( has_a => $pre )
201             or $class->meta_info( might_have => $pre ) )
202             {
203 0         0 croak "$pre is not a has_a or might_have rel on $class";
204             }
205             }
206              
207 15         85 my ( $sql_parts, $classes, $columns, $values ) =
208             $class->_search( $criteria, $attributes );
209              
210 15         184 my $cache_key;
211              
212 15 50 33     99 if ( $class->cache && $attributes->{use_resultset_cache} ) {
213              
214 0         0 my $sql = join '', @{$sql_parts}{qw/ where from order_by limit /};
  0         0  
215              
216 0         0 $cache_key =
217             $class->_resultset_cache_key( $sql, $values,
218             $attributes->{prefetch} );
219 0         0 my $cache_entry;
220              
221 0         0 my ($latest_stale) = sort { $b <=> $a }
  0         0  
222 0         0 grep defined, map { $class->cache->get($_) }
223 0         0 grep defined, map { $_->_staleness_cache_key } values %{$classes};
  0         0  
224              
225 0 0       0 if ($cache_key) {
226              
227 0 0       0 if ( $cache_entry = $class->_live_resultset_cache->{$cache_key} ) {
228              
229 0 0 0     0 if ( $cache_entry->{created} <= ( $latest_stale || 0 ) ) {
230              
231 0         0 delete $class->_live_resultset_cache->{$cache_key};
232 0         0 undef $cache_entry;
233             }
234             else {
235              
236             # So reset doesn't screw the original copy
237             # (which might still be in scope and in use)
238              
239 0         0 $cache_entry =
240              
241             {
242             %$cache_entry,
243             iterator => bless(
244 0         0 { %{ $cache_entry->{iterator} } },
245             ref $cache_entry->{iterator}
246             )
247             };
248              
249 0         0 $cache_entry->{iterator}->reset;
250             }
251             }
252              
253 0 0 0     0 if ( !( defined $cache_entry )
254             and $cache_entry = $class->cache->get($cache_key) )
255             {
256              
257 0 0 0     0 if ( $cache_entry->{created} <= ( $latest_stale || 0 ) ) {
258              
259 0         0 $class->cache->remove($cache_key);
260 0         0 undef $cache_entry;
261             }
262             else {
263              
264 0         0 $class->_live_resultset_cache->{$cache_key} = $cache_entry;
265             }
266              
267             }
268             }
269              
270 0 0       0 if ($cache_entry) {
271              
272 0         0 push (
273 0 0       0 @{ $class->profiling_data->{resultset_cache} },
274             [ 'HIT', $cache_key ]
275             )
276             if $attributes->{profile_cache};
277 0         0 my $iterator =
278             $class->_slice_iter( $attributes, $cache_entry->{iterator} );
279 0 0       0 return map $class->construct($_), $iterator->data if wantarray;
280 0         0 return $iterator;
281             }
282             push (
283 0 0       0 @{ $class->profiling_data->{resultset_cache} },
  0         0  
284             [ 'MISS', $cache_key ]
285             )
286             if $attributes->{profile_cache};
287             }
288              
289 15         125 my $pre_fields = ''; # Used in SELECT
290 15         21 my $pre_names = ''; # for use in GROUP BY
291              
292 15 50       46 if ( $attributes->{prefetch} ) {
293 0         0 $pre_fields .= ", '"
294 0         0 . join ( ' ', @{ $attributes->{prefetch} } )
295             . "' AS sweet__joins";
296              
297 0         0 my $jnum = 0;
298 0         0 foreach my $pre ( @{ $attributes->{prefetch} } ) {
  0         0  
299 0         0 $jnum++;
300 0         0 my $f_class = $classes->{$pre};
301 0         0 foreach my $col ( $f_class->columns('Essential') ) {
302 0         0 $pre_names .= ", ${pre}.${col}";
303 0         0 $pre_fields .= ", ${pre}.${col} AS sweet__${jnum}_${col}";
304             }
305             }
306             }
307              
308 15         36 $sql_parts->{prefetch_cols} = $pre_fields;
309 15         93 $sql_parts->{prefetch_names} = $pre_names;
310              
311 15   50     82 my $sql_method = 'sql_' . ( $attributes->{sql_method} || 'Join_Retrieve' );
312              
313 15   50     82 my $statement_order = $attributes->{statement_order}
314             || [qw/ prefetch_cols from sql /];
315              
316 15         26 my @sql_parts;
317 15         31 for my $part (@$statement_order) {
318              
319             # For backward compatibility
320 45 100       100 if ( $part eq 'sql' ) {
321 15         52 push @sql_parts, join ' ',
322 15         23 @{$sql_parts}{qw/ where order_by limit/};
323 15         37 next;
324             }
325 30 50       71 if ( exists $sql_parts->{$part} ) {
326 30         49 push @sql_parts, $sql_parts->{$part};
327 30         46 next;
328             }
329 0         0 die "'statement_order' setting of [$part] is invalid";
330             }
331              
332 15         91 my $sth = $class->$sql_method(@sql_parts);
333              
334 15         9372 $class->_bind_param( $sth, $columns );
335              
336 15         463 my $iterator = $class->sth_to_objects( $sth, $values );
337              
338 15 50 33     5283 if ( $class->cache && $attributes->{use_resultset_cache} ) {
339              
340 0         0 my $cache_entry = {
341             created => time(),
342 0         0 iterator => bless( { %{$iterator} }, ref $iterator )
343             };
344              
345 0         0 $class->cache->set( $cache_key, $cache_entry );
346 0         0 $class->_live_resultset_cache->{$cache_key} = $cache_entry;
347             }
348              
349 15         175 $iterator = $class->_slice_iter( $attributes, $iterator );
350              
351 15 100       77 return map $class->construct($_), $iterator->data if wantarray;
352 3         30 return $iterator;
353             }
354              
355             sub _slice_iter {
356 15     15   29 my ( $class, $attributes, $iterator ) = @_;
357              
358             # Create pager if doesn't already exist
359 15 100 100     71 if ( exists $attributes->{_pager} && !$attributes->{_pager} ) {
360              
361 1         11 $attributes->{_pager} =
362             Data::Page->new( $iterator->count, $attributes->{rows},
363             $attributes->{page}, );
364              
365 1         72 $attributes->{offset} = $attributes->{_pager}->skipped;
366             }
367              
368             # If RDBM is not ROWS/OFFSET supported, slice iterator
369 15 100 100     231 if ( $attributes->{rows} && $iterator->count > $attributes->{rows} ) {
370              
371 1         9 my $rows = $attributes->{rows};
372 1   50     4 my $offset = $attributes->{offset} || 0;
373              
374 1         7 $iterator = $iterator->slice( $offset, $offset + $rows - 1 );
375             }
376              
377 15         320 return $iterator;
378             }
379              
380             sub _search {
381 22     22   1023 my $proto = shift;
382 22         34 my $criteria = shift;
383 22         36 my $attributes = shift;
384 22   66     244 my $class = ref($proto) || $proto;
385              
386             # Valid SQL::Abstract params
387 22         50 my %params = map { $_ => $attributes->{$_} } qw(case cmp convert logic);
  88         269  
388              
389 22         61 $params{cdbi_class} = $class;
390 22         49 $params{cdbi_me_alias} = 'me';
391              
392             # Overide bindtype, we need all columns and values for deflating
393 22         197 my $abstract =
394             Class::DBI::Sweet::SQL::Abstract->new( %params, bindtype => 'columns' );
395              
396 22         1527 my ( $sql, $from, $classes, @bind ) =
397             $abstract->where( $criteria, '', $attributes->{prefetch} );
398              
399 22         54 my ( @columns, @values, %cache );
400              
401 22         43 foreach my $bind (@bind) {
402 24         48 push ( @columns, $bind->[0] );
403 24         45 push ( @values, @{$bind}[ 1 .. $#$bind ] );
  24         64  
404             }
405              
406 22 100       114 unless ( $sql =~ /^\s*WHERE/i )
407             { # huh? This is either WHERE.. or empty string.
408 3         7 $sql = "WHERE 1=1 $sql";
409             }
410              
411 22         103 $sql =~ s/^\s*(WHERE)\s*//i;
412              
413 22         126 my %sql_parts = (
414             where => $sql,
415             from => $from,
416             limit => '',
417             order_by => '',
418             );
419              
420 22 100       1294 $sql_parts{order_by} = $abstract->_order_by( $attributes->{order_by} )
421             if $attributes->{order_by};
422              
423 22 100 100     972 if ( $attributes->{rows} && !$attributes->{disable_sql_paging} ) {
424              
425 6         13 my $rows = $attributes->{rows};
426 6   50     31 my $offset = $attributes->{offset} || 0;
427 6         30 my $driver = lc $class->db_Main->{Driver}->{Name};
428              
429 6 50       568 if ( $driver =~ /^(maxdb|mysql|mysqlpp)$/ ) {
    50          
    0          
430 0         0 $sql_parts{limit} = ' LIMIT ?, ?';
431 0         0 push ( @columns, '__OFFSET', '__ROWS' );
432 0         0 push ( @values, $offset, $rows );
433             }
434              
435             elsif ( $driver =~ /^(pg|pgpp|sqlite|sqlite2)$/ ) {
436 6         15 $sql_parts{limit} = ' LIMIT ? OFFSET ?';
437 6         13 push ( @columns, '__ROWS', '__OFFSET' );
438 6         14 push ( @values, $rows, $offset );
439             }
440              
441             elsif ( $driver =~ /^(interbase)$/ ) {
442 0         0 $sql_parts{limit} = ' ROWS ? TO ?';
443 0         0 push ( @columns, '__ROWS', '__OFFSET' );
444 0         0 push ( @values, $rows, $offset + $rows );
445             }
446             }
447              
448 22         174 return ( \%sql_parts, $classes, \@columns, \@values );
449             }
450              
451             sub _search_args {
452 24     24   53 my $proto = shift;
453              
454 24         46 my ( $criteria, $attributes );
455              
456 24 100 100     351 if ( @_ == 2
    100 66        
      66        
457             && ref( $_[0] ) =~ /^(ARRAY|HASH)$/
458             && ref( $_[1] ) eq 'HASH' )
459             {
460 13         28 $criteria = $_[0];
461 13         26 $attributes = $_[1];
462             }
463             elsif ( @_ == 1 && ref( $_[0] ) =~ /^(ARRAY|HASH)$/ ) {
464 8         14 $criteria = $_[0];
465 8         19 $attributes = {};
466             }
467             else {
468 3 50       13 $attributes = @_ % 2 ? pop (@_) : {};
469 3         11 $criteria = {@_};
470             }
471              
472             # Need to pass things in $attributes, so don't create a new hash
473 24         43 for my $key ( keys %{ $proto->default_search_attributes } ) {
  24         151  
474 0   0     0 $attributes->{$key} ||= $proto->default_search_attributes->{$key};
475             }
476              
477 24         2494 return ( $criteria, $attributes );
478             }
479              
480             #----------------------------------------------------------------------
481             # CACHING
482             #----------------------------------------------------------------------
483              
484             __PACKAGE__->mk_classdata('cache');
485              
486             sub cache_key {
487 0     0 1 0 my $proto = shift;
488 0   0     0 my $class = ref($proto) || $proto;
489 0         0 my $data;
490              
491 0         0 my @primary_columns = $class->primary_columns;
492              
493 0 0       0 if (@_) {
494 0 0 0     0 if ( @_ == 1 && ref( $_[0] ) eq 'HASH' ) {
    0          
495 0         0 $data = $_[0];
496             }
497             elsif ( @_ == 1 ) {
498 0         0 $data = { $primary_columns[0] => $_[0] };
499             }
500             else {
501 0         0 $data = {@_};
502             }
503             }
504             else {
505 0         0 @{$data}{@primary_columns} = $proto->get(@primary_columns);
  0         0  
506             }
507              
508 0 0       0 unless ( @primary_columns == grep defined, @{$data}{@primary_columns} ) {
  0         0  
509 0         0 return;
510             }
511              
512 0         0 return join "|", $class, map $_ . '=' . $data->{$_}, sort @primary_columns;
513             }
514              
515             sub _resultset_cache_key {
516 0     0   0 my ( $class, $sql, $values, $prefetch ) = @_;
517              
518 0 0       0 $class = ref $class if ref $class;
519              
520 0 0       0 my @pre = map { "=${_}"; } @{ $prefetch || [] };
  0         0  
  0         0  
521              
522 0         0 my $it = $class->iterator_class;
523              
524 0 0       0 return join "|", $class, "=${sql}", "=${it}", @pre, @{ $values || [] };
  0         0  
525             }
526              
527             sub _staleness_cache_key {
528 0     0   0 my ($class) = @_;
529              
530 0 0       0 $class = ref $class if ref $class;
531              
532 0         0 return "${class}|+staleness_key";
533             }
534              
535             sub _init {
536 40     40   32177 my $class = shift;
537              
538 40   100     128 my $data = $_[0] || {};
539              
540 40 50 33     270 unless ( $class->cache || $data->{'sweet__joins'} ) {
541 40         644 return $class->SUPER::_init(@_);
542             }
543              
544 0         0 my $key = $class->cache_key($data);
545              
546 0         0 my $object;
547              
548 0 0 0     0 if ( $class->cache and $key and $object = $class->cache->get($key) ) {
      0        
549 0 0       0 push ( @{ $class->profiling_data->{object_cache} }, [ 'HIT', $key ] )
  0         0  
550             if ( $class->default_search_attributes->{profile_cache} );
551              
552             # ensure that objects from the cache get inflated properly
553 0 0       0 if ( ( caller(1) )[3] eq "Class::DBI::_simple_bless" ) {
554 0         0 $object->call_trigger('select');
555             }
556              
557 0         0 return $object;
558             }
559              
560 0 0       0 push ( @{ $class->profiling_data->{object_cache} }, [ 'MISS', $key ] )
  0         0  
561             if ( $class->default_search_attributes->{profile_cache} );
562              
563 0         0 $object = bless {}, $class;
564              
565 0 0       0 if ( my $joins = $data->{'sweet__joins'} ) {
566 0         0 my $meta = $class->meta_info;
567 0         0 my $jnum = 0;
568 0         0 foreach my $join ( split ( / /, $joins ) ) {
569 0         0 my ( $rel, $f_class );
570 0         0 $jnum++;
571 0 0       0 if ( $rel = $meta->{has_a}{$join} ) {
    0          
572 0         0 $f_class = $rel->foreign_class;
573 0         0 my %attrs =
574 0         0 map { ( $_ => $data->{"sweet__${jnum}_${_}"} ) }
575             $f_class->columns('Essential');
576 0         0 $data->{$join} = $f_class->construct( \%attrs );
577             }
578             elsif ( $rel = $meta->{might_have}{$join} ) {
579 0         0 $f_class = $rel->foreign_class;
580 0         0 my %attrs =
581 0         0 map { ( $_ => $data->{"sweet__${jnum}_${_}"} ) }
582             $f_class->columns('Essential');
583 0         0 $object->{"_${join}_object"} = $f_class->construct( \%attrs );
584             }
585             else {
586 0         0 croak("Unable to find relationship ${join} on ${class}");
587             }
588             }
589             }
590              
591 0         0 $object->_attribute_store(%$data);
592              
593 0 0 0     0 if ( $class->cache and $key ) {
594 0         0 $object->call_trigger('deflate_for_create');
595 0         0 $class->cache->set( $key, $object );
596             }
597              
598 0         0 return $object;
599             }
600              
601             sub retrieve {
602 5     5 1 93318 my $class = shift;
603              
604 5 50       39 if ( $class->cache ) {
605              
606 0 0       0 if ( my $key = $class->cache_key(@_) ) {
607              
608 0 0       0 if ( my $object = $class->cache->get($key) ) {
609 0         0 $object->call_trigger('select');
610 0         0 push (
611 0 0       0 @{ $class->profiling_data->{object_cache} },
612             [ 'HIT', $key ]
613             )
614             if ( $class->default_search_attributes->{profile_cache} );
615 0         0 return $object;
616             }
617              
618 0 0       0 push ( @{ $class->profiling_data->{object_cache} },
  0         0  
619             [ 'MISS', $key ] )
620             if ( $class->default_search_attributes->{profile_cache} );
621             }
622             }
623              
624 5         183 return $class->SUPER::retrieve(@_);
625             }
626              
627             *create = \&insert;
628              
629             sub insert {
630 6     6 1 349519 my $self = shift;
631              
632 6 50       125 if ( $self->cache ) {
633 0         0 $self->cache->set( $self->_staleness_cache_key, time() );
634             }
635              
636 6         235 return $self->SUPER::insert(@_);
637             }
638              
639             sub update {
640 1     1 1 2909 my $self = shift;
641              
642 1 50       5 if ( $self->cache ) {
643 0         0 $self->cache->remove( $self->cache_key );
644 0         0 $self->cache->set( $self->_staleness_cache_key, time() );
645             }
646              
647 1         16 return $self->SUPER::update(@_);
648             }
649              
650             sub delete {
651 0     0 1 0 my $self = shift;
652              
653 0 0       0 return $self->_search_delete(@_) if not ref $self;
654              
655 0 0       0 if ( $self->cache ) {
656 0         0 $self->cache->remove( $self->cache_key );
657 0         0 $self->cache->set( $self->_staleness_cache_key, time() );
658             }
659              
660 0         0 return $self->SUPER::delete(@_);
661             }
662              
663             #----------------------------------------------------------------------
664             # UNIVERSALLY UNIQUE IDENTIFIERS
665             #----------------------------------------------------------------------
666              
667             sub _next_in_sequence {
668 1     1   1835873 my $self = shift;
669              
670 1 50       6 if ( lc $self->sequence eq 'uuid' ) {
671              
672 1 50       50 die "UUID features not available" unless $UUID_Is_Available;
673              
674 1 50       7 if ( $^O eq 'MSWin32' ) {
675 0         0 return Win32API::GUID::CreateGuid();
676             }
677             else {
678 1         424 return Data::UUID->new->create_str;
679             }
680             }
681              
682 0         0 return $self->SUPER::_next_in_sequence;
683             }
684              
685             #----------------------------------------------------------------------
686             # MORE MAGIC
687             #----------------------------------------------------------------------
688              
689             package Class::DBI::Sweet::SQL::Abstract;
690              
691 57     57   401455 use base qw/SQL::Abstract/;
  57         178  
  57         100823  
692 57     57   945500 use Carp qw/croak/;
  57         148  
  57         119610  
693              
694             sub where {
695 22     22   65 my ( $self, $where, $order, $must_join ) = @_;
696              
697 22         71 my $me = $self->{cdbi_me_alias};
698 22         93 $self->{cdbi_table_aliases} = { $me => $self->{cdbi_class} };
699 22         124 $self->{cdbi_join_info} = {};
700 22         52 $self->{cdbi_column_cache} = {};
701              
702 22 50       32 foreach my $join ( @{ $must_join || [] } ) {
  22         121  
703 0         0 $self->_resolve_join( $me => $join );
704             }
705              
706 22         42 my $sql = '';
707              
708 22         103 my (@ret) = $self->_recurse_where($where);
709              
710 22 50       1356 if (@ret) {
711 22         35 my $wh = shift @ret;
712 22 100       94 $sql .= $self->_sqlcase(' where ') . $wh if $wh;
713             }
714              
715 22         154 $sql =~ s/(\S+)( IS(?: NOT)? NULL)/$self->_default_tables($1).$2/ge;
  1         6  
716              
717 22         58 my $joins = delete $self->{cdbi_join_info};
718 22         49 my $tables = delete $self->{cdbi_table_aliases};
719              
720 22         121 my $from = $self->{cdbi_class}->table . " ${me}";
721              
722 22         355 foreach my $join ( keys %{$joins} ) {
  22         76  
723 12         77 my $table = $tables->{$join}->table;
724 12         237 $from .= ", ${table} ${join}";
725 12         48 my ( $l_alias, $l_key, $f_key ) =
726 12         36 @{ $joins->{$join} }{qw/l_alias l_key f_key/};
727 12         58 $sql .= " AND ${l_alias}.${l_key} = ${join}.${f_key}";
728             }
729              
730             # order by?
731             #if ($order) {
732             # $sql .= $self->_order_by($order);
733             #}
734              
735 22         192 delete $self->{cdbi_column_cache};
736              
737 22 50       157 return wantarray ? ( $sql, $from, $tables, @ret ) : $sql;
738             }
739              
740             sub _convert {
741 48     48   7848 my ( $self, $to_convert ) = @_;
742              
743 48 100       188 return $self->SUPER::_convert($to_convert) if $to_convert eq '?';
744 24         291 return $self->SUPER::_convert( $self->_default_tables($to_convert) );
745             }
746              
747             sub _default_tables {
748 49     49   84 my ( $self, $to_convert ) = @_;
749              
750 49         86 my $alias = $self->{cdbi_me_alias};
751              
752 49         157 my @alias = split ( /\./, $to_convert );
753              
754 49         85 my $field = pop (@alias);
755              
756 49         96 foreach my $f_alias (@alias) {
757              
758 23 100       97 $self->_resolve_join( $alias => $f_alias )
759             unless $self->{cdbi_table_aliases}{$f_alias};
760 23         61 $alias = $f_alias;
761             }
762              
763 49 50       203 if ( my $meta = $self->{cdbi_class}->meta_info( has_many => $field ) ) {
764              
765 0         0 my $f_alias = $field;
766 0 0       0 $self->_resolve_join( $alias => $f_alias )
767             unless $self->{cdbi_table_aliases}{$f_alias};
768              
769 0         0 $field = ( ( $meta->foreign_class->columns('Primary') )[0] );
770 0         0 $alias = $f_alias;
771             }
772              
773 49         882 return "${alias}.${field}";
774             }
775              
776             sub _resolve_join {
777 12     12   66 my ( $self, $l_alias, $f_alias ) = @_;
778 12         27 my $l_class = $self->{cdbi_table_aliases}->{$l_alias};
779 12         62 my $meta = $l_class->meta_info;
780 12         156 my ( $rel, $f_class );
781 12 100       97 if ( $rel = $meta->{has_a}{$f_alias} ) {
    100          
    50          
782 2         8 $f_class = $rel->foreign_class;
783 2         22 $self->{cdbi_join_info}{$f_alias} = {
784             l_alias => $l_alias,
785             l_key => $f_alias,
786             f_key => ( $f_class->columns('Primary') )[0]
787             };
788             }
789             elsif ( $rel = $meta->{has_many}{$f_alias} ) {
790 6         32 $f_class = $rel->foreign_class;
791 6         59 $self->{cdbi_join_info}{$f_alias} = {
792             l_alias => $l_alias,
793             l_key => ( $l_class->columns('Primary') )[0],
794             f_key => $rel->args->{foreign_key}
795             };
796             }
797             elsif ( $rel = $meta->{might_have}{$f_alias} ) {
798 4         35 $f_class = $rel->foreign_class;
799 4         51 $self->{cdbi_join_info}{$f_alias} = {
800             l_alias => $l_alias,
801             l_key => ( $l_class->columns('Primary') )[0],
802             f_key => ( $f_class->columns('Primary') )[0]
803             };
804             }
805             else {
806 0         0 croak("Unable to find join info for ${f_alias} from ${l_class}");
807             }
808              
809 12         668 $self->{cdbi_table_aliases}{$f_alias} = $f_class;
810             }
811              
812             sub _bindtype {
813 24     24   207 my ( $self, $var, $val, @rest ) = @_;
814 24         115 $var = $self->_default_tables($var);
815 24         148 my ( $alias, $col ) = split ( /\./, $var );
816 24         61 my $f_class = $self->{cdbi_table_aliases}{$alias};
817              
818 24         70 my $column = $self->{cdbi_column_cache}{$alias}{$col};
819              
820 24 100       67 unless ($column) {
821              
822             $column = $f_class->find_column($col)
823 22   33 0   116 || ( List::Util::first { $_->accessor eq $col } $f_class->columns )
  0            
824             || croak("$col is not a column of ${f_class}");
825              
826 22         2874 $self->{cdbi_column_cache}{$alias}{$col} = $column;
827             }
828              
829 24 100       119 if ( ref $val eq $f_class ) {
830 2         6 my $accessor = $column->accessor;
831 2         14 $val = $val->$accessor;
832             }
833              
834 24         258 $val = $f_class->_deflated_column( $column, $val );
835              
836 24         430 return $self->SUPER::_bindtype( $var, $val, @rest );
837             }
838              
839             1;
840              
841             __END__