File Coverage

blib/lib/Rose/DB/Object/Manager.pm
Criterion Covered Total %
statement 126 1646 7.6
branch 36 956 3.7
condition 10 536 1.8
subroutine 24 57 42.1
pod 14 16 87.5
total 210 3211 6.5


line stmt bran cond sub pod time code
1             package Rose::DB::Object::Manager;
2              
3 61     61   102366 use strict;
  61         139  
  61         1764  
4              
5 61     61   333 use Carp();
  61         142  
  61         1163  
6              
7 61     61   308 use List::Util qw(first);
  61         136  
  61         4314  
8             #use List::MoreUtils qw(uniq);
9 61     61   443 use Scalar::Util qw(weaken refaddr);
  61         135  
  61         3385  
10              
11 61     61   27340 use Rose::DB::Object::Iterator;
  61         229  
  61         2311  
12 61     61   40185 use Rose::DB::Object::QueryBuilder qw(build_select build_where_clause);
  61         227  
  61         4913  
13             use Rose::DB::Object::Constants
14 61     61   490 qw(PRIVATE_PREFIX STATE_LOADING STATE_IN_DB MODIFIED_COLUMNS);
  61         156  
  61         4072  
15              
16             # XXX: A value that is unlikely to exist in a primary key column value
17 61     61   421 use constant PK_JOIN => "\0\2,\3\0";
  61         157  
  61         6249  
18              
19             our $VERSION = '0.813';
20              
21             our $Debug = 0;
22              
23             #
24             # Class data
25             #
26              
27             use Rose::Class::MakeMethods::Generic
28             (
29 61         751 inheritable_scalar =>
30             [
31             'error',
32             'total',
33             'error_mode',
34             '_object_class',
35             '_base_name',
36             '_default_manager_method_types',
37             'default_objects_per_page',
38             'default_limit_with_subselect',
39             'default_nested_joins',
40             'dbi_prepare_cached',
41             'strict_ops',
42             ],
43 61     61   1174 );
  61         6770  
44              
45             __PACKAGE__->error_mode('fatal');
46             __PACKAGE__->default_objects_per_page(20);
47             __PACKAGE__->default_limit_with_subselect(1);
48             __PACKAGE__->default_nested_joins(1);
49             __PACKAGE__->dbi_prepare_cached(0);
50             __PACKAGE__->strict_ops(0);
51             __PACKAGE__->default_manager_method_types(qw(objects iterator count delete update));
52              
53             sub handle_error
54             {
55 0     0 0 0 my($class, $object) = @_;
56              
57 0         0 my $mode = $class->error_mode;
58              
59 0 0       0 return if($mode eq 'return');
60              
61 0         0 my $level = $Carp::CarpLevel;
62 0         0 local $Carp::CarpLevel = $level + 1;
63              
64 0 0 0     0 if($mode eq 'croak' || $mode eq 'fatal')
    0          
    0          
    0          
65             {
66 0         0 Carp::croak $object->error;
67             }
68             elsif($mode eq 'carp')
69             {
70 0         0 Carp::carp $object->error;
71             }
72             elsif($mode eq 'cluck')
73             {
74 0         0 Carp::croak $object->error;
75             }
76             elsif($mode eq 'confess')
77             {
78 0         0 Carp::confess $object->error;
79             }
80             else
81             {
82 0         0 Carp::croak "(Invalid error mode set: '$mode') - ", $object->error;
83             }
84              
85 0         0 return 1;
86             }
87              
88             sub normalize_get_objects_args
89             {
90             # Handle all these arg forms:
91             #
92             # get_objects(a => b, c => d, ...);
93             # get_objects([ ... ], a => b, c => d, ...)
94             # get_objects({ ... }, a => b, c => d, ...)
95              
96 0 0   0 1 0 if(ref $_[1])
97             {
98 0         0 my $class = shift;
99              
100 0 0       0 if(ref $_[0] eq 'HASH')
    0          
101             {
102 0         0 return ($class, query => [ %{shift(@_)} ], @_);
  0         0  
103             }
104             elsif(ref $_[0] eq 'ARRAY')
105             {
106 0         0 return ($class, query => shift, @_);
107             }
108 0         0 else { Carp::croak 'Invalid arguments: ', join(', ', @_) }
109             }
110              
111 0         0 return @_;
112             }
113              
114             # XXX: These are duplicated from ManyToMany.pm because I don't want to use()
115             # XXX: that module from here if I don't have to. Lazy or foolish? Hm.
116             # XXX: Anyway, make sure they stay in sync!
117 61     61   64492 use constant MAP_RECORD_METHOD => 'map_record';
  61         183  
  61         4357  
118 61     61   474 use constant DEFAULT_REL_KEY => PRIVATE_PREFIX . '_default_rel_key';
  61         172  
  61         40281  
119              
120       0 1   sub object_class { }
121              
122             sub default_manager_method_types
123             {
124 63     63 1 392 my($class) = shift;
125              
126 63 100       491 if(@_)
127             {
128 62 50 33     367 if(@_ == 1 && ref $_[0] eq 'ARRAY')
129             {
130 0         0 $class->_default_manager_method_types(@_);
131             }
132             else
133             {
134 62         404 $class->_default_manager_method_types([ @_ ]);
135             }
136             }
137              
138             return wantarray ?
139 63 100       1661 @{$class->_default_manager_method_types} : $class->_default_manager_method_types;
  1         7  
140             }
141              
142             sub make_manager_methods
143             {
144 1     1 1 4 my($class) = shift;
145              
146 1 50       5 if(@_ == 1)
147             {
148 1         10 @_ = (methods => { $_[0] => [ $class->default_manager_method_types ] });
149             }
150             else
151             {
152 0 0       0 Carp::croak "make_manager_methods() called with an odd number of arguments"
153             unless(@_ % 2 == 0);
154             }
155              
156 1         39 my %args = @_;
157              
158 1 50       5 local $Debug = $args{'debug'} if(exists $args{'debug'});
159              
160 1 50       5 my $calling_class = ($class eq __PACKAGE__) ? (caller)[0] : $class;
161 1   33     7 my $target_class = $args{'target_class'} || $calling_class;
162 1         3 my $object_class = $args{'object_class'};
163 1 50       5 my $class_invocant = UNIVERSAL::isa($target_class, __PACKAGE__) ?
164             $target_class : __PACKAGE__;
165              
166 1 50       4 unless($object_class)
167             {
168 1 50       5 if(UNIVERSAL::isa($target_class, 'Rose::DB::Object::Manager'))
169             {
170 1         32 $object_class = $target_class->object_class;
171             }
172              
173 1 50 33     6 if(!$object_class && UNIVERSAL::isa($target_class, 'Rose::DB::Object'))
174             {
175 0         0 $object_class = $target_class;
176             }
177             }
178              
179 1 50       5 unless($object_class)
180             {
181 0 0       0 Carp::croak "Could not determine object class. Please pass a value for ",
182             "the object_class parameter",
183             (UNIVERSAL::isa($target_class, 'Rose::DB::Object::Manager') ?
184             " or override the object_class() method in $target_class" : '');
185             }
186              
187 1 50       6 unless(UNIVERSAL::isa($object_class, 'Rose::DB::Object'))
188             {
189 0         0 my $error;
190              
191             TRY:
192             {
193 0         0 local $@;
  0         0  
194 0         0 eval "require $object_class";
195 0         0 $error = $@;
196             }
197              
198 0 0       0 if($error)
199             {
200 0         0 Carp::croak "Could not load object class $object_class - $error";
201             }
202             }
203              
204 1         4 my $meta = $object_class->meta;
205 1         4 my $cm = $meta->convention_manager;
206              
207 1   33     13 my $base_name = $args{'base_name'} || $cm->auto_manager_base_name($meta->table, $object_class);
208              
209 1 50       12 if(!$args{'methods'})
    50          
210             {
211 0 0       0 unless($base_name)
212             {
213 0         0 Carp::croak "Missing methods parameter and base_name parameter, and the ",
214             "convention manager's auto_manager_base_name() method did not ",
215             "return a true value"
216             }
217              
218 0         0 $args{'methods'} =
219             {
220             $base_name => [ $class->default_manager_method_types ]
221             };
222             }
223             elsif($args{'base_name'})
224             {
225 0         0 Carp::croak "Please pass the methods parameter OR the base_name parameter, not both";
226             }
227              
228             Carp::croak "Invalid 'methods' parameter - should be a hash ref"
229 1 50       17 unless(ref $args{'methods'} eq 'HASH');
230              
231 1         11 $class->_base_name($base_name);
232 1         16 $class->_object_class($object_class);
233              
234 1         9 while(my($name, $types) = each %{$args{'methods'}})
  2         40  
235             {
236 1 50       3 $class->_base_name($name) unless($base_name);
237              
238 1 50       6 my $have_full_name = ($name =~ s/\(\)$//) ? 1 : 0;
239              
240 1 50 33     8 Carp::croak "Invalid value for the '$name' parameter"
241             if(ref $types && ref $types ne 'ARRAY');
242              
243 1 0 33     4 if($have_full_name && ref $types && @$types > 1)
      33        
244             {
245 0         0 Carp::croak "Cannot use explicit method name $name() with more ",
246             "than one method type";
247             }
248              
249 1 50       5 foreach my $type ((ref $types ? @$types : ($types)))
250             {
251 61     61   541 no strict 'refs';
  61         157  
  61         62210  
252              
253 3 50       18 if($type eq 'objects')
    100          
    100          
    50          
    0          
254             {
255 0 0 0     0 my $method_name =
256             $have_full_name ? $name :
257             ($cm->auto_manager_method_name($type, $base_name, $object_class) || "get_$name");
258              
259 0         0 foreach my $class ($target_class, $class_invocant)
260             {
261 0         0 my $method = "${class}::$method_name";
262 0         0 my $short_method = $method_name;
263             Carp::croak "A $method method already exists"
264 0 0       0 if(defined &{$method});
  0         0  
265              
266 0 0       0 Carp::croak "The $short_method method is inherited from Rose::DB::Object::Manager ",
267             "and cannot be overriden in $target_class"
268             if(Rose::DB::Object::Manager->can($short_method));
269             }
270              
271 0 0       0 $Debug && warn "Making method: $target_class->$method_name()\n";
272 0         0 *{"${target_class}::$method_name"} = sub
273             {
274 0     0   0 shift;
275 0         0 $class_invocant->get_objects(@_, object_class => $object_class);
276 0         0 };
277             }
278             elsif($type eq 'count')
279             {
280 1 50 33     6 my $method_name =
281             $have_full_name ? $name :
282             ($cm->auto_manager_method_name($type, $base_name, $object_class) || "get_${name}_count");
283              
284 1         14 foreach my $class ($target_class, $class_invocant)
285             {
286 2         6 my $method = "${class}::$method_name";
287             Carp::croak "A $method method already exists"
288 2 50       3 if(defined &{$method});
  2         11  
289             }
290              
291 1 50       5 $Debug && warn "Making method: $target_class->$method_name()\n";
292 1         6 *{"${target_class}::$method_name"} = sub
293             {
294 0     0   0 shift;
295 0         0 $class_invocant->get_objects(
296             @_, count_only => 1, object_class => $object_class)
297 1         5 };
298             }
299             elsif($type eq 'iterator')
300             {
301 1 50 33     16 my $method_name =
302             $have_full_name ? $name :
303             ($cm->auto_manager_method_name($type, $base_name, $object_class) || "get_${name}_iterator");
304              
305 1         18 foreach my $class ($target_class, $class_invocant)
306             {
307 2         7 my $method = "${class}::$method_name";
308             Carp::croak "A $method method already exists"
309 2 50       4 if(defined &{$method});
  2         25  
310             }
311              
312 1 50       4 $Debug && warn "Making method: $target_class->$method_name()\n";
313 1         7 *{"${target_class}::$method_name"} = sub
314             {
315 0     0   0 shift;
316 0         0 $class_invocant->get_objects(
317             @_, return_iterator => 1, object_class => $object_class)
318 1         8 };
319             }
320             elsif($type eq 'delete')
321             {
322 1 50 33     14 my $method_name =
323             $have_full_name ? $name :
324             ($cm->auto_manager_method_name($type, $base_name, $object_class) || "delete_$name");
325              
326 1         15 foreach my $class ($target_class, $class_invocant)
327             {
328 2         6 my $method = "${class}::$method_name";
329             Carp::croak "A $method method already exists"
330 2 50       3 if(defined &{$method});
  2         13  
331             }
332              
333 1 50       5 $Debug && warn "Making method: $target_class->$method_name()\n";
334 1         7 *{"${target_class}::$method_name"} = sub
335             {
336 0     0   0 shift;
337 0         0 $class_invocant->delete_objects(@_, object_class => $object_class);
338 1         5 };
339             }
340             elsif($type eq 'update')
341             {
342 0 0 0     0 my $method_name =
343             $have_full_name ? $name :
344             ($cm->auto_manager_method_name($type, $base_name, $object_class) || "update_$name");
345              
346 0         0 foreach my $class ($target_class, $class_invocant)
347             {
348 0         0 my $method = "${class}::$method_name";
349             Carp::croak "A $method method already exists"
350 0 0       0 if(defined &{$method});
  0         0  
351             }
352              
353 0 0       0 $Debug && warn "Making method: $target_class->$method_name()\n";
354 0         0 *{"${target_class}::$method_name"} = sub
355             {
356 0     0   0 shift;
357 0         0 $class_invocant->update_objects(@_, object_class => $object_class);
358 0         0 };
359             }
360             else
361             {
362 0         0 Carp::croak "Invalid method type: $type";
363             }
364             }
365             }
366             }
367              
368             sub get_objects_count
369             {
370 0     0 1   my($class) = shift;
371 0           $class->get_objects(@_, count_only => 1);
372             }
373              
374 0     0 1   sub get_objects_iterator { shift->get_objects(@_, return_iterator => 1) }
375 0     0 1   sub get_objects_sql { shift->get_objects(@_, return_sql => 1) }
376              
377 61     61   577 use constant WITH => 555; # arbitrary
  61         199  
  61         52049  
378              
379             sub get_objects
380             {
381 0     0 1   my($class, %args);
382              
383 0 0         if(ref $_[1])
384             {
385 0           $class = shift;
386              
387 0 0         if(ref $_[0] eq 'HASH')
    0          
388             {
389 0           %args = (query => [ %{shift(@_)} ], @_);
  0            
390             }
391             elsif(ref $_[0] eq 'ARRAY')
392             {
393 0           %args = (query => shift, @_);
394             }
395 0           else { Carp::croak 'Invalid arguments: ', join(', ', @_) }
396              
397 0           unshift(@_, $class); # restore original args
398             }
399             else
400             {
401 0           ($class, %args) = @_;
402             }
403              
404 0           $class->error(undef);
405              
406 0   0       my $object_class = delete $args{'object_class'}
407             || $class->object_class || Carp::croak "Missing object class argument";
408              
409 0           my $return_sql = delete $args{'return_sql'};
410 0           my $return_iterator = delete $args{'return_iterator'};
411 0           my $count_only = delete $args{'count_only'};
412 0           my $require_objects = delete $args{'require_objects'};
413 0           my $with_objects = delete $args{'with_objects'};
414              
415 0   0       my $skip_first = delete $args{'skip_first'} || 0;
416 0           my $distinct = delete $args{'distinct'};
417 0           my $fetch = delete $args{'fetch_only'};
418 0   0       my $hints = delete $args{'hints'} || {};
419 0           my $select = $args{'select'};
420              
421             # Alias by popular demand...
422             $args{'query'} = delete $args{'where'}
423 0 0 0       if($args{'where'} && !exists $args{'query'});
424              
425 0 0         $args{'strict_ops'} = $class->strict_ops unless(exists $args{'strict_ops'});
426              
427 0           my $no_forced_sort = delete $args{'no_forced_sort'};
428              
429             my $table_aliases = exists $args{'table_aliases'} ?
430 0 0         $args{'table_aliases'} : ($args{'table_aliases'} = 1);
431              
432             # Coerce for_update boolean alias into lock argument
433 0 0         if(delete $args{'for_update'})
434             {
435 0   0       $args{'lock'}{'type'} ||= 'for update';
436             }
437              
438 0 0 0       $with_objects = undef if(ref $with_objects && !@$with_objects);
439 0 0 0       $require_objects = undef if(ref $require_objects && !@$require_objects);
440              
441 0 0         local $Debug = $args{'debug'} if(exists $args{'debug'});
442              
443             my $try_subselect_limit = (exists $args{'limit_with_subselect'}) ?
444 0 0         $args{'limit_with_subselect'} : $class->default_limit_with_subselect;
445              
446 0           my $subselect_limit = 0;
447              
448             # Can't do direct inject with custom select lists
449 0 0         my $direct_inject = $select ? 0 : delete $args{'inject_results'};
450              
451 0           my(%fetch, %rel_name, %di_keys);
452              
453 0           my $meta = $object_class->meta;
454              
455 0   0       $args{'hints'} = $hints->{'t1'} || $hints->{$meta->table} || $hints;
456              
457             my $prepare_cached =
458 0 0         exists $args{'prepare_cached'} ? $args{'prepare_cached'} :
459             $class->dbi_prepare_cached;
460              
461 0   0       my $db = delete $args{'db'} || $object_class->init_db;
462 0           my $dbh = delete $args{'dbh'};
463 0           my $dbh_retained = 0;
464              
465 0 0         unless($dbh)
466             {
467 0 0         unless($dbh = $db->retain_dbh)
468             {
469 0           $class->error($db->error);
470 0           $class->handle_error($class);
471 0           return undef;
472             }
473              
474 0           $dbh_retained = 1;
475             }
476              
477             # Work-around for http://rt.cpan.org//Ticket/Display.html?id=33193
478             local $dbh->{'pg_expand_array'} = 0
479 0 0 0       if($dbh->{'Driver'}{'Name'} eq 'Pg' && index($dbh->{'Driver'}{'Version'}, '2.0.') == 0);
480              
481             my $nested_joins = $args{'nested_joins'} = $db->supports_nested_joins ?
482 0 0         (defined $args{'nested_joins'} ? $args{'nested_joins'} : $class->default_nested_joins) : 0;
    0          
483              
484             my $use_explicit_joins = (defined $args{'explicit_joins'}) ?
485 0 0         $args{'explicit_joins'} : !$db->likes_implicit_joins;
486              
487 0           my $with_map_records;
488              
489 0 0         if($with_map_records = delete $args{'with_map_records'})
490             {
491 0 0         unless(ref $with_map_records)
492             {
493 0 0         if($with_map_records =~ /^[A-Za-z_]\w*$/)
    0          
494             {
495 0           $with_map_records = { DEFAULT_REL_KEY() => $with_map_records };
496             }
497             elsif($with_map_records)
498             {
499 0           $with_map_records = { DEFAULT_REL_KEY() => MAP_RECORD_METHOD };
500             }
501             else
502             {
503 0           $with_map_records = 0;
504             }
505             }
506             }
507              
508 0 0 0       my $outer_joins = ($with_objects && !$require_objects) ? 1 : 0;
509              
510 0           my($num_required_objects, %required_object, $num_with_objects,
511             %with_objects, @belongs_to, %seen_rel, %rel_tn, %join_type);
512              
513 0 0 0       $with_objects = [ $with_objects ] if($with_objects && !ref $with_objects);
514 0 0 0       $require_objects = [ $require_objects ] if($require_objects && !ref $require_objects);
515              
516             #print STDERR 'WITH: ', Dumper($with_objects);
517             #print STDERR 'REQUIRE: ', Dumper($require_objects);
518              
519             # XXX: Currently, the most robust join-type conflict checking only
520             # XXX: happens if a least one join-type override is present. In
521             # XXX: other cases, the "with" wins. This is "safe" but not
522             # XXX: necessarily efficient.
523              
524             # If there are any join-type overrides
525 0 0   0     if(first { index($_, '!') > 0 || index($_, '?') > 0 }
  0 0          
    0          
    0          
526             (($with_objects ? @$with_objects : ()),
527             ($require_objects ? @$require_objects : ())))
528             {
529 0           my $i = 0;
530 0 0         my $requires_start = $with_objects ? @$with_objects : 0;
531 0           my $in_require = 0;
532 0           my $join_type;
533              
534             # Pull out the join modifiers
535 0 0         foreach my $arg (($with_objects ? @$with_objects : ()),
    0          
536             ($require_objects ? @$require_objects : ()))
537             {
538 0 0 0       $in_require = 1 if(!$in_require && $i++ == $requires_start);
539              
540 0           my $save_arg = $arg;
541 0           $arg =~ tr/!?//d;
542              
543 0 0         if(index($arg, '.') < 0)
544             {
545 0           $save_arg =~ s/([!?])$//;
546              
547 61     61   568 no warnings 'uninitialized';
  61         146  
  61         7498  
548 0 0 0       $join_type = ($1 eq '!' || (!$1 && $in_require)) ? 'JOIN' : 'LEFT OUTER JOIN';
549              
550             Carp::croak "Conflicting suffix for '$arg' - please choose either ! or ?"
551 0 0 0       if($join_type{$arg} && $join_type{$arg} ne $join_type);
552              
553 0           $join_type{$arg} = $join_type;
554             }
555             else
556             {
557 0           $save_arg =~ s/([!?])$//;
558              
559 61     61   524 no warnings 'uninitialized';
  61         211  
  61         206628  
560 0 0 0       $join_type = ($1 eq '!' || (!$1 && $in_require)) ? 'JOIN' : 'LEFT OUTER JOIN';
561              
562             Carp::croak "Conflicting suffix for '$arg' - please choose either ! or ?"
563 0 0 0       if($join_type{$arg} && $join_type{$arg} ne $join_type);
564              
565 0           $join_type{$arg} = $join_type;
566              
567 0           while($save_arg =~ s/\.[^.]+$//)
568             {
569 0           $save_arg =~ s/([!?])$//;
570              
571 0 0 0       $join_type = ($1 eq '!' || (!$1 && $in_require)) ? 'JOIN' : 'LEFT OUTER JOIN';
572              
573 0           (my $clean_arg = $save_arg) =~ tr/!?//d;
574              
575             Carp::croak "Conflicting suffix for '$clean_arg' - please choose either ! or ?"
576 0 0 0       if($join_type{$clean_arg} && $join_type{$clean_arg} ne $join_type);
577              
578 0           $join_type{$clean_arg} = $join_type;
579             }
580             }
581             }
582              
583 0 0         if(grep { $_ eq 'LEFT OUTER JOIN' } values %join_type)
  0            
584             {
585 0           $outer_joins = 1;
586             }
587             }
588              
589             # Putting join conditions in the WHERE clause can change the meaning of
590             # the query when outer joins are used, so disable them in that case.
591             my $use_redundant_join_conditions =
592 0 0         $outer_joins ? 0 : delete $args{'redundant_join_conditions'};
593              
594             #use Data::Dumper;
595             #print STDERR 'JOIN TYPES: ', Dumper(\%join_type);
596             #print STDERR 'POST WITH: ', Dumper($with_objects);
597             #print STDERR 'POST REQUIRE: ', Dumper($require_objects);
598              
599 0 0         if($with_objects)
600             {
601             # Doing this implicitly is never a good idea
602             #unless(defined $use_redundant_join_conditions)
603             #{
604             # $use_redundant_join_conditions = $db->likes_redundant_join_conditions;
605             #}
606              
607             # Copy argument (shallow copy)
608 0           $with_objects = [ @$with_objects ]; #[ uniq @$with_objects ];
609              
610             # Expand multi-level arguments
611 0 0   0     if(first { index($_, '.') >= 0 } @$with_objects)
  0            
612             {
613 0           my @with_objects;
614              
615 0           foreach my $arg (@$with_objects)
616             {
617 0 0         next if($seen_rel{$arg});
618              
619 0 0         if(index($arg, '.') < 0)
620             {
621 0           $seen_rel{$arg} = WITH;
622 0           push(@with_objects, $arg);
623             }
624             else
625             {
626 0           my @expanded = ($arg);
627 0           $seen_rel{$arg} = WITH;
628              
629 0           while($arg =~ s/\.([^.]+)$//)
630             {
631 0 0         next if($seen_rel{$arg}++);
632 0           unshift(@expanded, $arg);
633             }
634              
635 0           push(@with_objects, @expanded);
636             }
637             }
638              
639 0           $with_objects = \@with_objects;
640             }
641             else
642             {
643 0           $seen_rel{$_} = WITH for(@$with_objects);
644             }
645              
646 0           $num_with_objects = @$with_objects;
647 0           %with_objects = map { $_ => 1 } @$with_objects;
  0            
648             }
649              
650 0 0         if($require_objects)
651             {
652             # Copy argument (shallow copy)
653 0           $require_objects = [ @$require_objects ]; #[ uniq @$require_objects ];
654              
655             # Expand multi-level arguments
656 0 0   0     if(first { index($_, '.') >= 0 } @$require_objects)
  0            
657             {
658 0           my @require_objects;
659              
660 0           foreach my $arg (@$require_objects)
661             {
662 0 0         if(index($arg, '.') < 0)
663             {
664 0 0         if(my $seen = $seen_rel{$arg})
665             {
666 0 0         if($seen == WITH)
667             {
668 0           Carp::croak "require_objects argument '$arg' conflicts with ",
669             "with_objects argument of the same name";
670             }
671 0           next;
672             }
673              
674 0           $seen_rel{$arg}++;
675 0           push(@require_objects, $arg);
676             }
677             else
678             {
679 0           my @expanded = ($arg);
680              
681 0 0         if(my $seen = $seen_rel{$arg})
682             {
683 0 0         if($seen == WITH)
684             {
685 0           Carp::croak "require_objects argument '$arg' conflicts with ",
686             "with_objects argument of the same name";
687             }
688 0           next;
689             }
690              
691 0           $seen_rel{$arg}++;
692              
693 0           while($arg =~ s/\.[^.]+$//)
694             {
695 0 0         next if($seen_rel{$arg});
696 0           unshift(@expanded, $arg);
697 0           $seen_rel{$arg}++;
698             }
699              
700 0           push(@require_objects, @expanded);
701             }
702             }
703              
704 0           $require_objects = \@require_objects;
705             }
706             else
707             {
708 0           foreach my $arg (@$require_objects)
709             {
710 0 0         if($seen_rel{$arg})
711             {
712 0           Carp::croak "require_objects argument '$arg' conflicts with ",
713             "with_objects argument of the same name";
714             }
715             }
716             }
717              
718 0           $num_required_objects = @$require_objects;
719 0           %required_object = map { $_ => 1 } @$require_objects;
  0            
720 0           push(@$with_objects, @$require_objects)
721             }
722              
723 0 0         my %object_args = (ref $args{'object_args'} eq 'HASH') ? %{$args{'object_args'}} : ();
  0            
724 0           my %subobject_args;
725              
726 0 0         $args{'share_db'} = 1 unless(exists $args{'share_db'});
727              
728 0 0         if(delete $args{'share_db'})
729             {
730 0           $object_args{'db'} = $db;
731 0           $subobject_args{'db'} = $db;
732             }
733              
734 0           my($fields, $fields_string, $table);
735              
736 0 0         $args{'nonlazy'} = [] unless(defined $args{'nonlazy'});
737 0           my $nonlazy = $args{'nonlazy'};
738 0 0         my %nonlazy = (ref $nonlazy ? map { $_ => 1 } @$nonlazy : ());
  0            
739              
740 0           my @tables = ($meta->fq_table($db));
741 0           my @tables_sql = ($meta->fq_table_sql($db));
742              
743 0 0 0       my $use_lazy_columns = (!ref $nonlazy || $nonlazy{'self'}) ? 0 : $meta->has_lazy_columns;
744              
745 0           my(%columns, %methods, %all_columns);
746              
747 0 0         if($use_lazy_columns)
748             {
749 0           %columns = ($tables[0] => scalar $meta->nonlazy_columns);
750 0           %all_columns = ($tables[0] => scalar $meta->columns);
751 0           %methods = ($tables[0] => scalar $meta->nonlazy_column_mutator_method_names);
752 0           %di_keys = ($object_class => scalar $meta->nonlazy_column_db_value_hash_keys);
753             }
754             else
755             {
756 0           %columns = ($tables[0] => scalar $meta->columns);
757 0           %methods = ($tables[0] => scalar $meta->column_mutator_method_names);
758 0           %di_keys = ($object_class => scalar $meta->column_db_value_hash_keys);
759             }
760              
761 0           my %classes = ($tables[0] => $object_class);
762 0           my @classes = ($object_class);
763 0           my %meta = ($object_class => $meta);
764              
765 0           my @table_names = ($meta->table);
766 0           my @rel_names = ($meta->table);
767              
768 0           my(@joins, @subobject_methods, @mapped_object_methods, $clauses);
769              
770 0           my $handle_dups = 0;
771             #my $deep_joins = 0;
772 0           my @has_dups;
773              
774 0           my $manual_limit = 0;
775              
776 0 0         my $num_subtables = $with_objects ? @$with_objects : 0;
777              
778 0 0 0       if($distinct || $fetch)
779             {
780 0 0 0       if($fetch && ref $distinct)
781             {
782 0           Carp::croak "The 'distinct' and 'fetch' parameters cannot be used ",
783             "together if they both contain lists of tables";
784             }
785              
786 0 0         $args{'distinct'} = 1 if($distinct);
787              
788 0           %fetch = (t1 => 1, $tables[0] => 1, $meta->table => 1);
789              
790 0 0 0       if(ref $fetch || ref $distinct)
791             {
792 0 0         foreach my $arg (ref $distinct ? @$distinct : @$fetch)
793             {
794 0           $fetch{$arg} = 1;
795             }
796             }
797             }
798              
799             # Handle "page" arguments
800 0 0 0       if(exists $args{'page'} || exists $args{'per_page'})
801             {
802 0 0 0       if(exists $args{'limit'} || exists $args{'offset'})
803             {
804 0           Carp::croak 'Cannot include the "page" or "per_page" ',
805             'options when the "limit" or "offset" option ',
806             'is used';
807             }
808              
809 0   0       my $page = delete $args{'page'} || 1;
810 0   0       my $per_page = delete $args{'per_page'} || $class->default_objects_per_page;
811              
812 0 0         $page = 1 if($page < 1);
813 0 0         $per_page = $class->default_objects_per_page if($per_page < 1);
814              
815 0           $args{'limit'} = $per_page;
816              
817 0 0         if($page > 1)
818             {
819 0           $args{'offset'} = ($page - 1) * $per_page;
820             }
821             }
822              
823             # Pre-process sort_by args
824 0 0         if(my $sort_by = $args{'sort_by'})
825             {
826 0 0         $sort_by = [ $sort_by ] unless(ref $sort_by eq 'ARRAY');
827              
828 0 0 0       if($num_subtables == 0 && defined $table_aliases && $table_aliases == 0)
      0        
829             {
830             # trim t1. or primary table prefixes
831 0           my $prefix_re = '\b(?:t1|' . $meta->table . ')\.';
832 0           $prefix_re = qr($prefix_re);
833              
834 0           foreach my $sort (@$sort_by)
835             {
836 0 0         $sort =~ s/$prefix_re//g unless(ref $sort);
837             }
838             }
839              
840 0           $args{'sort_by'} = $sort_by;
841             }
842              
843 0           my $num_to_many_rels = 0;
844             # Adjust for explicitly included map_record tables, which should
845             # not count towards the multi_many_ok warning.
846 0           my $num_to_many_rels_adjustment = 0;
847              
848 0           my($multi_many, @subobject_method_map);
849              
850 0 0         if($with_objects)
851             {
852             # XXX: Hack to avoid spurious ORA-00918 errors
853             # XXX: http://ora-00918.ora-code.com/msg/28663.html
854 0 0 0       if(($args{'limit'} || $args{'offset'}) && $dbh->{'Driver'}{'Name'} eq 'Oracle')
      0        
855             {
856 0           $args{'unique_aliases'} = 1;
857             }
858              
859             # Copy clauses arg
860 0 0         $clauses = $args{'clauses'} ? [ @{$args{'clauses'}} ] : [];
  0            
861              
862 0           my $i = 1;
863              
864             # Sanity check with_objects arguments, and determine if we're going to
865             # have to handle duplicate data from multiple joins. If so, note
866             # which with_objects arguments refer to relationships that may return
867             # more than one object.
868 0           foreach my $name (@$with_objects)
869             {
870 0           my $tn_name = $name;
871              
872 0 0         if(index($tn_name, '.') > 0) # dot at start is invalid, so "> 0" is correct
873             {
874 0           $tn_name =~ /^(.+)\.([^.]+)$/;
875             }
876              
877 0           $rel_tn{$tn_name} = $i + 1; # note the tN table number of this relationship
878              
879 0           my $key;
880              
881             # Chase down multi-level keys: e.g., colors.name.types
882 0 0         if(index($name, '.') >= 0)
883             {
884             #$deep_joins = 1;
885              
886 0           my $chase_meta = $meta;
887              
888 0           while($name =~ /\G([^.]+)(?:\.|$)/g)
889             {
890 0           my $sub_name = $1;
891              
892 0   0       $key = $chase_meta->foreign_key($sub_name) ||
893             $chase_meta->relationship($sub_name) ||
894             Carp::confess "Invalid with_objects or require_objects argument: ",
895             "no foreign key or relationship named '$sub_name' ",
896             'found in ', $chase_meta->class;
897              
898 0 0         $chase_meta = $key->can('foreign_class') ?
899             $key->foreign_class->meta : $key->class->meta;
900             }
901             }
902             else
903             {
904 0   0       $key = $meta->foreign_key($name) || $meta->relationship($name) ||
905             Carp::confess "Invalid with_objects or require_objects argument: ",
906             "no foreign key or relationship named '$name' ",
907             "found in $class";
908             }
909              
910 0           my $rel_type = $key->type;
911              
912 0 0         if($rel_type =~ /\bmany$/)
913             {
914 0           $handle_dups = 1;
915 0           $has_dups[$i] = 1;
916              
917             # "many to many" relationships have an extra table (the mapping table)
918 0 0         if($rel_type eq 'many to many')
919             {
920 0           $i++;
921 0           $has_dups[$i] = 1;
922             # $num_subtables will be incremented elsewhere (below)
923              
924             # Adjust for explicitly included map_record tables, which should
925             # not count towards the multi_many_ok warning.
926 0           $num_to_many_rels_adjustment++;
927             }
928              
929 0 0         if($args{'limit'})
930             {
931 0 0 0       if($try_subselect_limit && $db->supports_select_from_subselect &&
      0        
      0        
      0        
932             (!$args{'offset'} || $db->supports_limit_with_offset) &&
933             !$args{'select'})
934             {
935 0 0 0       unless($fetch && @$fetch && $fetch->[0] eq 't1')
      0        
936             {
937 0           $subselect_limit = 1;
938 0           delete $args{'limit'};
939 0           delete $args{'offset'};
940             }
941             }
942             else
943             {
944 0           $manual_limit = delete $args{'limit'};
945             }
946             }
947              
948             # This restriction seems unnecessary now
949             #if($required_object{$name} && $num_required_objects > 1 && $num_subtables > 1)
950             #{
951             # Carp::croak
952             # qq(The "require_objects" parameter cannot be used with ),
953             # qq(a "... to many" relationship ("$name" in this case) ),
954             # qq(unless that relationship is the only one listed and ),
955             # qq(the "with_objects" parameter is not used);
956             #}
957             }
958              
959 0           $i++;
960             }
961              
962 0           $num_to_many_rels = grep { defined $_ } @has_dups;
  0            
963              
964             # Adjust for explicitly included map_record tables, which should
965             # not count towards the multi_many_ok warning.
966 0 0         $multi_many = (($num_to_many_rels - $num_to_many_rels_adjustment) > 1) ? 1 : 0;
967              
968 0 0         unless($args{'multi_many_ok'})
969             {
970 0 0         if($multi_many)
971             {
972 0           Carp::carp
973             qq(WARNING: Fetching sub-objects via more than one ),
974             qq("one to many" relationship in a single query may ),
975             qq(produce many redundant rows, and the query may be ),
976             qq(slow. If you're sure you want to do this, you can ),
977             qq(silence this warning by using the "multi_many_ok" ),
978             qq(parameter\n);
979             }
980             }
981              
982 0           $i = 1; # reset iterator for second pass through with_objects
983              
984             # Build lists of columns, classes, methods, and join conditions for all
985             # of the with_objects and/or require_objects arguments.
986 0           foreach my $arg (@$with_objects)
987             {
988 0           my($parent_meta, $parent_tn, $name);
989              
990 0 0         if(index($arg, '.') > 0) # dot at start is invalid, so "> 0" is correct
991             {
992 0           $arg =~ /^(.+)\.([^.]+)$/;
993 0           my $parent = $1;
994 0           $name = $2;
995              
996             # value of $i as of last iteration
997 0 0         $parent_tn = defined $rel_tn{$parent} ? $rel_tn{$parent}: $i;
998              
999 0           $belongs_to[$i] = $parent_tn - 1;
1000 0           $parent_meta = $classes[$parent_tn - 1]->meta;
1001             }
1002             else
1003             {
1004 0           $parent_meta = $meta;
1005 0           $name = $arg;
1006 0           $parent_tn = 1;
1007 0           $belongs_to[$i] = 0;
1008             }
1009              
1010             #$rel_tn{$arg} = $i + 1; # note the tN table number of this relationship
1011              
1012 0   0       my $rel = $parent_meta->foreign_key($name) ||
1013             $parent_meta->relationship($name) ||
1014             Carp::croak "No relationship named '$name' in class ",
1015             $parent_meta->class;
1016              
1017 0           my $rel_type = $rel->type;
1018              
1019 0 0 0       if($rel_type eq 'foreign key' || $rel_type eq 'one to one' ||
    0 0        
      0        
1020             $rel_type eq 'many to one' || $rel_type eq 'one to many')
1021             {
1022 0 0         my $ft_class = $rel->class
1023             or Carp::confess "$class - Missing foreign object class for '$name'";
1024              
1025 0           my $ft_columns = $rel->key_columns;
1026              
1027 0 0 0       if(!$ft_columns && $rel_type ne 'one to many')
1028             {
1029 0           Carp::confess "$class - Missing key columns for '$name'";
1030             }
1031              
1032 0 0 0       if($rel->can('query_args') && (my $query_args = $rel->query_args))
1033             {
1034             # (Re)map query parameters to the correct table
1035             # t1 -> No change (the primary table)
1036             # t2 -> The foreign table
1037 0           for(my $i = 0; $i < @$query_args; $i += 2)
1038             {
1039 0           my $param = $query_args->[$i];
1040              
1041 0 0         if(ref $param)
1042             {
1043 0           push(@{$args{'query'}}, $param);
  0            
1044 0           $i--;
1045 0           next;
1046             }
1047              
1048 0 0         unless($param =~ s/^t2\./t$rel_tn{$arg}./)
1049             {
1050 0 0         $param = "t$rel_tn{$arg}.$param" unless($param =~ /^t\d+\./);
1051             }
1052              
1053 0           push(@{$args{'query'}}, $param, $query_args->[$i + 1]);
  0            
1054             }
1055             }
1056              
1057 0           my $ft_meta = $ft_class->meta;
1058              
1059 0           $meta{$ft_class} = $ft_meta;
1060              
1061 0           push(@tables, $ft_meta->fq_table($db));
1062 0           push(@tables_sql, $ft_meta->fq_table_sql($db));
1063 0           push(@rel_names, $rel_name{'t' . (scalar @tables)} = $rel->name);
1064 0           push(@table_names, $ft_meta->table);
1065 0           push(@classes, $ft_class);
1066              
1067             # Iterator will be the tN value: the first sub-table is t2, and so on
1068 0           $i++;
1069              
1070 0 0 0       my $use_lazy_columns = (!ref $nonlazy || $nonlazy{$name}) ? 0 : $ft_meta->has_lazy_columns;
1071              
1072 0 0         if($use_lazy_columns)
1073             {
1074 0           $columns{$tables[-1]} = $ft_meta->nonlazy_columns;
1075 0           $all_columns{$tables[-1]} = $ft_meta->columns;
1076 0           $methods{$tables[-1]} = $ft_meta->nonlazy_column_mutator_method_names;
1077 0           $di_keys{$classes[-1]} = $ft_meta->nonlazy_column_db_value_hash_keys;
1078             }
1079             else
1080             {
1081 0           $columns{$tables[-1]} = $ft_meta->columns;
1082 0           $methods{$tables[-1]} = $ft_meta->column_mutator_method_names;
1083 0           $di_keys{$classes[-1]} = $ft_meta->column_db_value_hash_keys;
1084             }
1085              
1086 0           $classes{$tables[-1]} = $ft_class;
1087              
1088 0 0 0       $subobject_methods[$i - 1] =
1089             $direct_inject ? $rel->hash_key :
1090             $rel->method_name('get_set') ||
1091             $rel->method_name('get_set_now') ||
1092             $rel->method_name('get_set_on_save') ||
1093             Carp::confess "No 'get_set', 'get_set_now', or 'get_set_on_save' ",
1094             "method found for $rel_type '$name' in class ",
1095             $rel->parent->class;
1096              
1097             #$subobject_keys[$i - 1] = $rel->hash_key;
1098              
1099             # Reset each() iterator
1100             #keys(%$ft_columns);
1101              
1102 0           my(@redundant, @redundant_null);
1103              
1104 0 0 0       unless($ft_columns && %$ft_columns)
1105             {
1106 0 0         if($with_objects{$arg})
    0          
1107             {
1108 0   0       $joins[$i]{'type'} = $join_type{$arg} || 'LEFT OUTER JOIN';
1109             }
1110             elsif($use_explicit_joins)
1111             {
1112 0   0       $joins[$i]{'type'} = $join_type{$arg} || 'JOIN';
1113             }
1114             }
1115              
1116             # Add join condition(s)
1117 0           while(my($local_column, $foreign_column) = each(%$ft_columns))
1118             {
1119             # Use outer joins to handle duplicate or optional information.
1120             # Foreign keys that have all non-null columns are not outer-
1121             # joined when nested joins are enabled, however.
1122 0 0 0       if(!($rel_type eq 'foreign key' && $rel->is_required &&
      0        
      0        
1123             $rel->referential_integrity && $nested_joins) &&
1124             ($outer_joins || $with_objects{$arg}))
1125             {
1126             # Aliased table names
1127 0           push(@{$joins[$i]{'conditions'}}, "t${parent_tn}.$local_column = t$i.$foreign_column");
  0            
1128              
1129 0 0         if($multi_many)
1130             {
1131 0           my $local_method = $parent_meta->column_mutator_method_name($local_column);
1132 0           my $foreign_method = $ft_meta->column_accessor_method_name($foreign_column);
1133 0           push(@{$subobject_method_map[$i][$belongs_to[$i - 1]]}, [ $local_method, $foreign_method ]);
  0            
1134             }
1135              
1136             # Fully-qualified table names
1137             #push(@{$joins[$i]{'conditions'}}, "$tables[0].$local_column = $tables[-1].$foreign_column");
1138              
1139 0   0       $joins[$i]{'type'} = $join_type{$arg} || 'LEFT OUTER JOIN';
1140 0   0       $joins[$i]{'hints'} = $hints->{"t$i"} || $hints->{$name};
1141              
1142             # MySQL is stupid about using its indexes when "JOIN ... ON (...)"
1143             # conditions are the only ones given, so the code below adds some
1144             # redundant WHERE conditions. They should only be added when they
1145             # do not change the meaning of the query, in which case they
1146             # should nudge MySQL into using its indexes.
1147             # The clauses: "((<ON conditions>) OR (<any columns are null>))"
1148             # We build the two clauses separately in the loop below, then
1149             # combine it all after the loop is done.
1150 0 0         if($use_redundant_join_conditions)
1151             {
1152             # Aliased table names
1153 0           push(@redundant, "t${parent_tn}.$local_column = t$i.$foreign_column");
1154 0 0         push(@redundant_null, ($has_dups[$i - 1] ?
1155             "t$i.$foreign_column IS NULL" :
1156             "t${parent_tn}.$local_column IS NULL"));
1157              
1158             # Fully-qualified table names
1159             #push(@redundant, "$tables[$parent_tn - 1].$local_column = $tables[-1].$foreign_column");
1160             #push(@redundant_null, ($has_dups[$i - 1] ?
1161             # "$tables[-1].$foreign_column IS NULL" :
1162             # "$tables[$parent_tn - 1].$local_column IS NULL"));
1163             }
1164             }
1165             else
1166             {
1167 0 0         if($use_explicit_joins)
1168             {
1169             # Aliased table names
1170 0           push(@{$joins[$i]{'conditions'}}, "t${parent_tn}.$local_column = t$i.$foreign_column");
  0            
1171              
1172             # Fully-qualified table names
1173             #push(@{$joins[$i]{'conditions'}}, "$tables[$parent_tn - 1].$local_column = $tables[-1].$foreign_column");
1174              
1175 0   0       $joins[$i]{'type'} = $join_type{$arg} || 'JOIN';
1176 0   0       $joins[$i]{'hints'} = $hints->{"t$i"} || $hints->{$name};
1177             }
1178             else # implicit join with no ON clause
1179             {
1180             # Aliased table names
1181 0           push(@$clauses, "t${parent_tn}.$local_column = t$i.$foreign_column");
1182              
1183             # Fully-qualified table names
1184             #push(@$clauses, "$tables[$parent_tn - 1].$local_column = $tables[-1].$foreign_column");
1185             }
1186             }
1187             }
1188              
1189 0 0 0       $joins[$i]{'parent_tn'} = $parent_tn if($joins[$i] && $joins[$i]{'type'} eq 'JOIN');
1190              
1191             # XXX: Undocumented for now...
1192 0 0 0       if($rel->can('join_args') && (my $join_args = $rel->join_args))
1193             {
1194 0           my $cond =
1195             build_where_clause(dbh => $dbh,
1196             tables => [ @tables[$parent_tn - 1, $i - 1] ],
1197             columns => \%columns,
1198             all_columns => \%all_columns,
1199             classes => \%classes,
1200             meta => \%meta,
1201             db => $db,
1202             pretty => $Debug,
1203             query => $join_args);
1204              
1205             # XXX: Ugly hack...
1206 0           for($cond)
1207             {
1208 0           s/(?:^| )@{[ $tables[$parent_tn - 1] ]}\./t$parent_tn./mg;
  0            
1209 0           s/(?:^| )@{[ $tables[$i - 1] ]}\./t$i./mg;
  0            
1210 0           s/(?:^| )t1\./t$parent_tn./mg;
1211 0           s/(?:^| )t2\./t$i./mg;
1212 0           s/^\s\s+/ /mg;
1213 0           s/\A\s+//;
1214 0           s/\n/ /g;
1215             }
1216              
1217 0           push(@{$joins[$i]{'conditions'}}, $cond);
  0            
1218             }
1219              
1220 0 0         if(@redundant)
1221             {
1222 0           push(@$clauses, '((' . join(' AND ', @redundant) . ') OR (' .
1223             join(' OR ', @redundant_null) . '))');
1224             }
1225              
1226 0 0 0       $joins[$i]{'conditions'} ||= [ '1 = 1' ] if($joins[$i]);
1227              
1228             # Add sub-object sort conditions
1229 0 0 0       if($rel->can('manager_args') && (my $mgr_args = $rel->manager_args))
1230             {
1231             # Don't bother sorting by columns if we're not even selecting them
1232 0 0 0       if($mgr_args->{'sort_by'} && (!%fetch ||
      0        
1233             ($fetch{$tables[-1]} && !$fetch{$rel_names[-1]})))
1234             {
1235             my $sort_by = ref $mgr_args->{'sort_by'} eq 'ARRAY' ?
1236 0 0         [ @{$mgr_args->{'sort_by'}} ] : [ $mgr_args->{'sort_by'} ];
  0            
1237              
1238 0           foreach my $sort (@$sort_by)
1239             {
1240 61     61   597 no warnings 'uninitialized';
  61         175  
  61         99052  
1241 0 0         $sort =~ s/^(['"`]?)(\w+)\1(\s+(?:ASC|DESC))?$/t$i.$1$2$1$3/i
1242             unless(ref $sort);
1243             }
1244              
1245 0           push(@{$args{'sort_by'}}, @$sort_by);
  0            
1246             }
1247             }
1248             }
1249             elsif($rel_type eq 'many to many')
1250             {
1251             #
1252             # First add table, columns, and clauses for the map table itself
1253             #
1254              
1255 0 0         my $map_class = $rel->map_class
1256             or Carp::confess "$class - Missing map class for '$name'";
1257              
1258 0           my $map_meta = $map_class->meta;
1259              
1260 0           $meta{$map_class} = $map_meta;
1261              
1262 0           push(@tables, $map_meta->fq_table($db));
1263 0           push(@tables_sql, $map_meta->fq_table_sql($db));
1264             # %rel_name gets the foreign table (below), not the map table here
1265 0           push(@rel_names, $rel->name);
1266 0           push(@table_names, $map_meta->table);
1267 0           push(@classes, $map_class);
1268              
1269 0   0       my $rel_mgr_args = $rel->manager_args || {};
1270              
1271 0           my $map_record_method;
1272 0           my $rel_map_record_method = $rel->map_record_method;
1273              
1274 0 0         if(my $rel_with_map_records = $rel_mgr_args->{'with_map_records'})
    0          
1275             {
1276             $map_record_method =
1277 0 0 0       ($with_map_records && exists $with_map_records->{$name}) ? $with_map_records->{$name} :
    0          
1278             $rel_map_record_method ? $rel_map_record_method : MAP_RECORD_METHOD;
1279             }
1280             elsif($with_map_records)
1281             {
1282             $map_record_method =
1283             exists $with_map_records->{$name} ? $with_map_records->{$name} :
1284 0 0 0       $with_map_records->{DEFAULT_REL_KEY()} || 0;
1285             }
1286              
1287 0 0         if($map_record_method)
1288             {
1289 0 0 0       my $use_lazy_columns = (!ref $nonlazy || $nonlazy{$name}) ? 0 : $map_meta->has_lazy_columns;
1290              
1291 0 0         if($use_lazy_columns)
1292             {
1293 0           $columns{$tables[-1]} = $map_meta->nonlazy_columns;
1294 0           $all_columns{$tables[-1]} = $map_meta->columns;
1295 0           $methods{$tables[-1]} = $map_meta->nonlazy_column_mutator_method_names;
1296 0           $di_keys{$classes[-1]} = $map_meta->nonlazy_column_db_value_hash_keys;
1297             }
1298             else
1299             {
1300 0           $columns{$tables[-1]} = $map_meta->columns;
1301 0           $methods{$tables[-1]} = $map_meta->column_mutator_method_names;
1302 0           $di_keys{$classes[-1]} = $map_meta->column_db_value_hash_keys;
1303             }
1304             }
1305             else
1306             {
1307 0           $columns{$tables[-1]} = []; # Don't fetch map class columns
1308 0           $methods{$tables[-1]} = [];
1309             }
1310              
1311 0           $classes{$tables[-1]} = $map_class;
1312              
1313 0           my $column_map = $rel->column_map;
1314              
1315             # Iterator will be the tN value: the first sub-table is t2, and so on.
1316             # Increase once for map table.
1317 0           $i++;
1318              
1319             # Increase the tN table number of this relationship as well
1320 0           $rel_tn{$arg} = $i + 1;
1321              
1322 0           $belongs_to[$i] = $belongs_to[$i - 1];
1323              
1324 0   0       $mapped_object_methods[$i - 1] = $map_record_method || 0;
1325              
1326 0 0         if($map_record_method)
1327             {
1328 0 0         my $ft_class = $rel->foreign_class
1329             or Carp::confess "$class - Missing foreign class for '$name'";
1330              
1331 0 0         if($ft_class->can($map_record_method))
1332             {
1333 0 0 0       if($direct_inject && (my $map_record_key = $ft_class->meta->map_record_method_key($map_record_method)))
1334             {
1335 0           $mapped_object_methods[$i - 1] = $map_record_key;
1336             }
1337             }
1338             else
1339             {
1340 0           my $map_record_key =
1341             Rose::DB::Object::Metadata::Relationship::ManyToMany::make_map_record_method(
1342             $ft_class, $map_record_method, $map_class);
1343              
1344 0 0 0       if($direct_inject && $mapped_object_methods[$i - 1])
1345             {
1346 0           $mapped_object_methods[$i - 1] = $map_record_key;
1347             }
1348             }
1349             }
1350              
1351             # Add join condition(s)
1352 0           while(my($local_column, $foreign_column) = each(%$column_map))
1353             {
1354             # Use outer joins to handle duplicate or optional information.
1355 0 0 0       if($outer_joins || $with_objects{$arg})
1356             {
1357             # Aliased table names
1358 0           push(@{$joins[$i]{'conditions'}}, "t$i.$local_column = t${parent_tn}.$foreign_column");
  0            
1359              
1360             # Fully-qualified table names
1361             #push(@{$joins[$i]{'conditions'}}, "$tables[-1].$local_column = $tables[$parent_tn - 1].$foreign_column");
1362              
1363 0   0       $joins[$i]{'type'} = $join_type{$arg} || 'LEFT OUTER JOIN';
1364 0   0       $joins[$i]{'hints'} = $hints->{"t$i"} || $hints->{$name};
1365             }
1366             else
1367             {
1368 0 0         if($use_explicit_joins)
1369             {
1370             # Aliased table names
1371 0           push(@{$joins[$i]{'conditions'}}, "t$i.$local_column = t${parent_tn}.$foreign_column");
  0            
1372              
1373             # Fully-qualified table names
1374             #push(@{$joins[$i]{'conditions'}}, "$tables[-1].$local_column = $tables[$parent_tn - 1].$foreign_column");
1375              
1376 0   0       $joins[$i]{'type'} = $join_type{$arg} || 'JOIN';
1377 0   0       $joins[$i]{'hints'} = $hints->{"t$i"} || $hints->{$name};
1378             }
1379             else # implicit join with no ON clause
1380             {
1381             # Aliased table names
1382 0           push(@$clauses, "t$i.$local_column = t${parent_tn}.$foreign_column");
1383              
1384             # Fully-qualified table names
1385             #push(@$clauses, "$tables[-1].$local_column = $tables[$parent_tn - 1].$foreign_column");
1386             }
1387             }
1388             }
1389              
1390 0 0 0       $joins[$i]{'parent_tn'} = $parent_tn if($joins[$i] && $joins[$i]{'type'} eq 'JOIN');
1391              
1392             #
1393             # Now add table, columns, and clauses for the foreign object
1394             #
1395              
1396 0           $num_subtables++; # Account for the extra table
1397              
1398 0 0         my $ft_class = $rel->foreign_class
1399             or Carp::confess "$class - Missing foreign class for '$name'";
1400              
1401 0           my $ft_meta = $ft_class->meta;
1402 0           $meta{$ft_class} = $ft_meta;
1403              
1404 0 0         my $map_to = $rel->map_to
1405             or Carp::confess "Missing map_to value for relationship '$name' ",
1406             "in clas $class";
1407              
1408 0   0       my $foreign_rel =
1409             $map_meta->foreign_key($map_to) || $map_meta->relationship($map_to) ||
1410             Carp::confess "No foreign key or relationship named '$map_to' ",
1411             "found in $map_class";
1412              
1413 0 0         my $ft_columns = $foreign_rel->key_columns
1414             or Carp::confess "$ft_class - Missing key columns for '$map_to'";
1415              
1416 0           push(@tables, $ft_meta->fq_table($db));
1417 0           push(@tables_sql, $ft_meta->fq_table_sql($db));
1418 0           push(@rel_names, $rel_name{'t' . (scalar @tables)} = $rel->name);
1419 0           push(@table_names, $ft_meta->table);
1420 0           push(@classes, $ft_class);
1421              
1422 0 0 0       my $use_lazy_columns = (!ref $nonlazy || $nonlazy{$name}) ? 0 : $ft_meta->has_lazy_columns;
1423              
1424 0 0         if($use_lazy_columns)
1425             {
1426 0           $columns{$tables[-1]} = $ft_meta->nonlazy_columns;
1427 0           $all_columns{$tables[-1]} = $ft_meta->columns;
1428 0           $methods{$tables[-1]} = $ft_meta->nonlazy_column_mutator_method_names;
1429 0           $di_keys{$classes[-1]} = $ft_meta->nonlazy_column_db_value_hash_keys;
1430             }
1431             else
1432             {
1433 0           $columns{$tables[-1]} = $ft_meta->columns;
1434 0           $methods{$tables[-1]} = $ft_meta->column_mutator_method_names;
1435 0           $di_keys{$classes[-1]} = $ft_meta->column_db_value_hash_keys;
1436             }
1437              
1438 0           $classes{$tables[-1]} = $ft_class;
1439              
1440             # Iterator will be the tN value: the first sub-table is t2, and so on.
1441             # Increase again for foreign table.
1442 0           $i++;
1443              
1444 0 0 0       $subobject_methods[$i - 1] =
1445             $direct_inject ? $rel->hash_key :
1446             $rel->method_name('get_set') ||
1447             $rel->method_name('get_set_now') ||
1448             $rel->method_name('get_set_on_save') ||
1449             Carp::confess "No 'get_set', 'get_set_now', or 'get_set_on_save' ",
1450             "method found for relationship '$name' in class ",
1451             "$class";
1452              
1453             #$subobject_keys[$i - 1] = $rel->hash_key;
1454              
1455             # Reset each() iterator
1456             #keys(%$ft_columns);
1457              
1458             # Add join condition(s)
1459 0           while(my($local_column, $foreign_column) = each(%$ft_columns))
1460             {
1461             # Use left joins if the map table used an outer join above
1462 0 0 0       if($outer_joins || $with_objects{$arg})
1463             {
1464             # Aliased table names
1465 0           push(@{$joins[$i]{'conditions'}}, 't' . ($i - 1) . ".$local_column = t$i.$foreign_column");
  0            
1466              
1467             # Fully-qualified table names
1468             #push(@{$joins[$i]{'conditions'}}, "$tables[-2].$local_column = $tables[-1].$foreign_column");
1469              
1470 0   0       $joins[$i]{'type'} = $join_type{$arg} || 'LEFT OUTER JOIN';
1471 0   0       $joins[$i]{'hints'} = $hints->{"t$i"} || $hints->{$name};
1472             }
1473             else
1474             {
1475 0 0         if($use_explicit_joins)
1476             {
1477             # Aliased table names
1478 0           push(@{$joins[$i]{'conditions'}}, 't' . ($i - 1) . ".$local_column = t$i.$foreign_column");
  0            
1479              
1480             # Fully-qualified table names
1481             #push(@{$joins[$i]{'conditions'}}, "$tables[-2].$local_column = $tables[-1].$foreign_column");
1482              
1483 0   0       $joins[$i]{'type'} = $join_type{$arg} || 'JOIN';
1484 0   0       $joins[$i]{'hints'} = $hints->{"t$i"} || $hints->{$name};
1485             }
1486             else # implicit join with no ON clause
1487             {
1488             # Aliased table names
1489 0           push(@$clauses, 't' . ($i - 1) . ".$local_column = t$i.$foreign_column");
1490              
1491             # Fully-qualified table names
1492             #push(@$clauses, "$tables[-2].$local_column = $tables[-1].$foreign_column");
1493             }
1494             }
1495             }
1496              
1497 0 0 0       $joins[$i]{'parent_tn'} = $i - 1 if($joins[$i] && $joins[$i]{'type'} eq 'JOIN');
1498              
1499             # Add sub-object sort conditions
1500 0 0 0       if($rel->can('manager_args') && (my $mgr_args = $rel->manager_args))
1501             {
1502             # Don't bother sorting by columns if we're not even selecting them
1503 0 0 0       if($mgr_args->{'sort_by'} && (!%fetch ||
      0        
1504             ($fetch{$tables[-1]} && !$fetch{$rel_names[-1]})))
1505             {
1506             my $sort_by = ref $mgr_args->{'sort_by'} eq 'ARRAY' ?
1507 0 0         [ @{$mgr_args->{'sort_by'}} ] : [ $mgr_args->{'sort_by'} ];
  0            
1508              
1509             # translate un-prefixed simple columns
1510 0           foreach my $sort (@$sort_by)
1511             {
1512 61     61   626 no warnings 'uninitialized';
  61         168  
  61         125295  
1513 0 0         $sort =~ s/^(['"`]?)(\w+)\1(\s+(?:ASC|DESC))?$/t$i.$1$2$1$3/i
1514             unless(ref $sort);
1515             }
1516              
1517 0           push(@{$args{'sort_by'}}, @$sort_by);
  0            
1518             }
1519             }
1520             }
1521             else
1522             {
1523 0           Carp::croak "Don't know how to auto-join relationship '$name' of type '$rel_type'";
1524             }
1525             }
1526              
1527 0           $args{'clauses'} = $clauses; # restore clauses arg
1528             }
1529              
1530             # Flesh out list of fetch tables and cull columns for those tables
1531 0 0         if(%fetch)
1532             {
1533 0           foreach my $i (1 .. $#tables) # skip first table, which is always selected
1534             {
1535 0           my $tn = 't' . ($i + 1);
1536 0   0       my $rel_name = $rel_name{$tn} || '';
1537              
1538 0           (my $trimmed_table = $tables[$i]) =~ s/^[^.]+\.//;
1539              
1540 0 0 0       unless($fetch{$tn} || $fetch{$tables[$i]} || $fetch{$trimmed_table} ||
      0        
      0        
      0        
1541             $fetch{$rel_names[$i]} || $fetch{$rel_name})
1542             {
1543 0           $columns{$tables[$i]} = [];
1544 0           $methods{$tables[$i]} = [];
1545             }
1546             }
1547             }
1548              
1549 0           $args{'table_map'} = { reverse %rel_tn };
1550              
1551 0           my %tn;
1552              
1553 0 0         if($select)
1554             {
1555 0 0         if($fetch)
1556             {
1557 0           Carp::croak "The 'select' and 'fetch' parameters cannot be used together";
1558             }
1559              
1560 0 0         $select = [ split(/\s*,\s*/, $select) ] unless(ref $select);
1561              
1562 0           my $i = 1;
1563 0           %tn = map { $_ => $i++ } @table_names; # @tables;
  0            
1564 0           my $expand_dotstar = 0;
1565              
1566 0           foreach my $item (@$select)
1567             {
1568 0           my($column, $tn);
1569              
1570 0 0         next if(ref $item eq 'SCALAR');
1571              
1572 0 0 0       if(index($item, '.') < 0 && $item !~ /\s+ AS \s+ \w+ \s* \Z/xi)
    0          
    0          
1573             {
1574 0 0         $expand_dotstar = 1 if($item eq '*');
1575 0           $column = $item;
1576 0 0         $item = "t1.$item" if($table_aliases > 0);
1577 0           $tn = 1;
1578             }
1579             elsif($item =~ /^t(\d+)\.(.+)$/)
1580             {
1581 0           $tn = $1;
1582 0 0         $item = $2 unless($table_aliases);
1583 0           $column = $2;
1584 0 0         $expand_dotstar = 1 if($item =~ /^t\d+\.\*$/);
1585             }
1586             elsif($item =~ /^(['"]?)([^.(]+)\1\.(['"]?)(.+)(\3)$/)
1587             {
1588 0   0       my $num = $tn{$2} || $rel_tn{$2};
1589 0           $item = "t$num.$3$4$5";
1590 0           $tn = $num;
1591 0           $column = $4;
1592 0 0         $expand_dotstar = 1 if($item =~ /^t\d+\.\*$/);
1593             }
1594              
1595 0 0         if(defined $tn)
1596             {
1597 0           my $meta = $meta{$classes{$tables[$tn - 1]}};
1598              
1599 0 0 0       if($meta->column($column) && (my $alias = $meta->column($column)->alias))
1600             {
1601 0 0         $item .= ' AS ' . $alias unless($alias eq $column);
1602             }
1603             }
1604             }
1605              
1606             # Expand tN.* specifiers, if necessary
1607 0 0         if($expand_dotstar)
1608             {
1609 0           my @select;
1610              
1611 0           foreach my $item (@$select)
1612             {
1613 0 0         next if(ref $item eq 'SCALAR');
1614              
1615 0 0         unless($item =~ /^(?: t(\d+)\. )? \* $/x)
1616             {
1617 0           push(@select, $item);
1618 0           next;
1619             }
1620              
1621 0   0       my $tn = $1 || 1;
1622 0           my $meta = $meta{$classes{$tables[$tn - 1]}};
1623 0 0         my $prefix = $table_aliases ? "t$tn." : '';
1624              
1625 0           foreach my $column ($meta->columns)
1626             {
1627 0 0         if(my $alias = $column->alias)
1628             {
1629 0           push(@select, "$prefix$column AS $alias");
1630             }
1631             else
1632             {
1633 0           push(@select, "$prefix$column");
1634             }
1635             }
1636             }
1637              
1638 0           $select = \@select;
1639             }
1640              
1641 0           $args{'select'} = $select;
1642             }
1643              
1644 0 0         if($count_only)
1645             {
1646 0           delete $args{'limit'};
1647 0           delete $args{'offset'};
1648 0           delete $args{'sort_by'};
1649              
1650 0           my($sql, $bind, @bind_params);
1651              
1652             # Do we have to use DISTINCT to count?
1653 0 0 0       my $use_distinct = (delete $args{'distinct'} || $with_objects) ? 1 : 0;
1654              
1655 0 0 0       if(!$use_distinct && $require_objects)
1656             {
1657 0           foreach my $name (@$require_objects)
1658             {
1659             # Ignore error here since it'll be caught and handled later anyway
1660 0   0       my $key =
1661             $meta->foreign_key($name) || $meta->relationship($name) || next;
1662              
1663 0           my $rel_type = $key->type;
1664              
1665 0 0         if(index($key->type, 'many') >= 0)
1666             {
1667 0           $use_distinct = 1;
1668 0           last;
1669             }
1670             }
1671             }
1672              
1673             BUILD_SQL:
1674             {
1675 0           my($select, $wrap);
  0            
1676              
1677 0           my $pk_columns = $meta->primary_key_column_names;
1678              
1679 0 0 0       if(!$use_distinct || @$pk_columns == 1 ||
      0        
1680             $db->supports_multi_column_count_distinct)
1681             {
1682 0           my $select_arg = delete $args{'select'};
1683              
1684             $select = $use_distinct ?
1685 0 0         'COUNT(DISTINCT ' . join(', ', ($select_arg ? @$select_arg : (map { "t1.$_" } @$pk_columns))) . ')' :
  0 0          
1686             'COUNT(*)';
1687             }
1688             else
1689             {
1690             $select = $use_distinct ?
1691 0 0         'DISTINCT ' . join(', ', map { "t1.$_" } @$pk_columns) : 'COUNT(*)';
  0            
1692 0           $wrap = 1;
1693             }
1694              
1695 0           local $Carp::CarpLevel = $Carp::CarpLevel + 1;
1696              
1697 0           ($sql, $bind) =
1698             build_select(dbh => $dbh,
1699             select => $select,
1700             tables => \@tables,
1701             tables_sql => \@tables_sql,
1702             columns => \%columns,
1703             all_columns => \%all_columns,
1704             classes => \%classes,
1705             joins => \@joins,
1706             meta => \%meta,
1707             db => $db,
1708             pretty => $Debug,
1709             bind_params => \@bind_params,
1710             object_class => $object_class,
1711             %args);
1712              
1713 0 0         if($wrap)
1714             {
1715 0           $sql = "SELECT COUNT(*) FROM ($sql) sq";
1716             }
1717             }
1718              
1719 0 0         if($return_sql)
1720             {
1721 0 0         $db->release_dbh if($dbh_retained);
1722 0 0         return wantarray ? ($sql, $bind) : $sql;
1723             }
1724              
1725 0           my $count = 0;
1726              
1727 0           my $error;
1728              
1729             TRY:
1730             {
1731 0           local $@;
  0            
1732              
1733             eval
1734 0           {
1735 0           local $dbh->{'RaiseError'} = 1;
1736 0 0         $Debug && warn "$sql (", join(', ', @$bind), ")\n";
1737 0 0         my $sth = $prepare_cached ? $dbh->prepare_cached($sql, undef, 3) :
1738             $dbh->prepare($sql);
1739              
1740 0 0         if(@bind_params)
1741             {
1742 0           my $i = 1;
1743              
1744 0           foreach my $value (@$bind)
1745             {
1746 0           $sth->bind_param($i, $value, $bind_params[$i - 1]);
1747 0           $i++;
1748             }
1749              
1750 0           $sth->execute;
1751             }
1752             else
1753             {
1754 0           $sth->execute(@$bind);
1755             }
1756              
1757 0           ($count) = $sth->fetchrow_array;
1758 0           $sth->finish;
1759             };
1760              
1761 0           $error = $@;
1762             }
1763              
1764 0 0         if($error)
1765             {
1766 0           $class->total(undef);
1767 0 0         $class->error(ref $error ? $error : "get_objects() - $error");
1768 0           $class->handle_error($class);
1769 0           return undef;
1770             }
1771              
1772 0           $class->total($count);
1773 0           return $count;
1774             }
1775              
1776             # Post-process sort_by args
1777 0 0 0       if(my $sort_by = $args{'sort_by'})
    0 0        
      0        
1778             {
1779             # Alter sort_by SQL, replacing table and relationship names with aliases.
1780             # This is to prevent databases like PostgreSQL from "adding missing FROM
1781             # clause"s. See: http://sql-info.de/postgresql/postgres-gotchas.html#1_5
1782 0 0         if($table_aliases)
1783             {
1784 0           my $i = 0;
1785              
1786 0           foreach my $table (@tables)
1787             {
1788 0           $i++; # Table aliases are 1-based
1789              
1790 0           my $table_unquoted = $db->unquote_table_name($table);
1791              
1792             # Conditionalize schema part, if necessary
1793 0           $table_unquoted =~ s/^([^.]+\.)/(?:\Q$1\E)?/;
1794              
1795 0           foreach my $sort (grep { !ref } @$sort_by)
  0            
1796             {
1797 61     61   613 no warnings 'uninitialized';
  61         238  
  61         246416  
1798 0 0 0       unless($sort =~ s/^(['"`]?)(\w+)\1(\s+(?:ASC|DESC))?$/t1.$1$2$1$3/i ||
1799             $sort =~ s/\b$table_unquoted\./t$i./g)
1800             {
1801 0 0         if(my $rel_name = $rel_name{"t$i"})
1802             {
1803 0 0         $sort =~ s/\b$rel_name\./t$i./g unless($rel_name =~ /^t\d+$/);
1804             }
1805             }
1806             }
1807             }
1808              
1809             # When selecting sub-objects via a "... to many" relationship, force
1810             # a sort by t1's primary key unless sorting by some other column in
1811             # t1. This is required to ensure that all result rows from each row
1812             # in t1 are grouped together. But don't do it when we're selecting
1813             # columns from just one table. (Compare to 3 because the primary table
1814             # name, fully-qualified name, and the "t1" alias are always in the list.)
1815 0 0 0       if($num_to_many_rels > 0 && (!%fetch || (keys %fetch || 0) > 3) && !$no_forced_sort)
      0        
      0        
1816             {
1817 0           my $do_prefix = 1;
1818              
1819 0           foreach my $sort (@$sort_by)
1820             {
1821 0 0 0       if(!ref $sort && $sort =~ /^t1\./)
1822             {
1823 0           $do_prefix = 0;
1824 0           last;
1825             }
1826             }
1827              
1828 0 0         if($do_prefix)
1829             {
1830 0           unshift(@$sort_by, join(', ', map { "t1.$_" } $meta->primary_key_column_names));
  0            
1831             }
1832             }
1833             }
1834             else # otherwise, trim t1. prefixes
1835             {
1836 0           my $prefix_re = '\b(?:t1|' . $meta->table . ')\.';
1837 0           $prefix_re = qr($prefix_re);
1838              
1839 0           foreach my $sort (@$sort_by)
1840             {
1841 0 0         $sort =~ s/$prefix_re//g unless(ref $sort);
1842             }
1843             }
1844              
1845             # TODO: remove duplicate/redundant sort conditions
1846 0           $args{'sort_by'} = $sort_by;
1847             }
1848             elsif($num_to_many_rels > 0 && (!%fetch || (keys %fetch || 0) > 3) && !$no_forced_sort)
1849             {
1850             # When selecting sub-objects via a "... to many" relationship, force a
1851             # sort by t1's primary key to ensure that all result rows from each
1852             # row in t1 are grouped together. But don't do it when we're selecting
1853             # columns from just one table. (Compare to 3 because the primary table
1854             # name, fully-qualified name, and the "t1" alias are always in the list.)
1855 0           $args{'sort_by'} = [ join(', ', map { "t1.$_" } $meta->primary_key_column_names) ];
  0            
1856             }
1857              
1858 0 0         if(defined $args{'offset'})
    0          
1859             {
1860             Carp::croak "Offset argument is invalid without a limit argument"
1861 0 0 0       unless($args{'limit'} || $manual_limit);
1862              
1863 0 0 0       if($db->supports_limit_with_offset && !$manual_limit && !$subselect_limit)
    0 0        
1864             {
1865 0           $db->format_limit_with_offset($args{'limit'}, $args{'offset'}, \%args);
1866             #$args{'limit'} = $db->format_limit_with_offset($args{'limit'}, $args{'offset'});
1867             #delete $args{'offset'};
1868 0           $skip_first = 0;
1869             }
1870             elsif($manual_limit)
1871             {
1872 0           $skip_first += delete $args{'offset'};
1873             }
1874             else
1875             {
1876 0           $skip_first += delete $args{'offset'};
1877 0           $args{'limit'} += $skip_first;
1878 0           $db->format_limit_with_offset($args{'limit'}, undef, \%args);
1879             #$args{'limit'} = $db->format_limit_with_offset($args{'limit'});
1880             }
1881             }
1882             elsif($args{'limit'})
1883             {
1884 0           $db->format_limit_with_offset($args{'limit'}, undef, \%args);
1885             #$args{'limit'} = $db->format_limit_with_offset($args{'limit'});
1886             }
1887              
1888 0           my($count, @objects, $iterator);
1889              
1890 0           my($sql, $bind, @bind_params);
1891              
1892             BUILD_SQL:
1893             {
1894 0           local $Carp::CarpLevel = $Carp::CarpLevel + 1;
  0            
1895              
1896 0           ($sql, $bind) =
1897             build_select(dbh => $dbh,
1898             tables => \@tables,
1899             tables_sql => \@tables_sql,
1900             columns => \%columns,
1901             all_columns => \%all_columns,
1902             classes => \%classes,
1903             joins => \@joins,
1904             meta => \%meta,
1905             db => $db,
1906             pretty => $Debug,
1907             bind_params => \@bind_params,
1908             object_class => $object_class,
1909             %args);
1910              
1911 0 0         if($subselect_limit)
1912             {
1913 0           my($class, %sub_args) = @_;
1914              
1915             # The sort clause is important, so it can't be deleted, but it
1916             # also can't contain references to any table but t1.
1917 0 0 0       if($args{'sort_by'} && $num_subtables > 0)
1918             {
1919 0           my @sort_by;
1920              
1921 0           foreach my $arg (@{$args{'sort_by'}})
  0            
1922             {
1923 0 0         push(@sort_by, $arg) if(index((ref $arg ? $$arg : $arg), 't1.') == 0);
    0          
1924             }
1925              
1926 0           $sub_args{'sort_by'} = \@sort_by;
1927             }
1928              
1929             # Not safe to delete this if the query references columns in these tables
1930             #delete $sub_args{'with_objects'};
1931              
1932 0           $sub_args{'fetch_only'} = [ 't1' ];
1933 0           $sub_args{'from_and_where_only'} = 1;
1934              
1935 0           my @t1_bind_params;
1936 0           $sub_args{'bind_params'} = \@t1_bind_params;
1937              
1938 0           my($t1_sql, $t1_bind) = $class->get_objects_sql(%sub_args);
1939              
1940 0           my $columns = $sub_args{'select'};
1941              
1942 0 0         unless($columns)
1943             {
1944             my $multi_table =
1945             ($sub_args{'with_objects'} && (!ref $sub_args{'with_objects'} || @{$sub_args{'with_objects'}})) ||
1946 0   0       ($sub_args{'require_objects'} && (!ref $sub_args{'require_objects'} || @{$sub_args{'require_objects'}}));
1947              
1948 0 0         if($multi_table)
1949             {
1950 0           $table_aliases = 1;
1951             }
1952             else
1953             {
1954 0 0         $table_aliases = $multi_table unless(defined $table_aliases);
1955             }
1956              
1957             $columns = $table_aliases ?
1958 0           join(', ', map { "t1.$_" } @{$columns{$tables[0]}}) :
  0            
1959 0 0         join(', ', map { $_ } @{$columns{$tables[0]}});
  0            
  0            
1960             }
1961              
1962 0 0 0       my $distinct = ($num_with_objects && scalar @{[ @has_dups[1 .. $num_with_objects] ]}) ? ' DISTINCT' : '';
1963              
1964 0           $t1_sql = "SELECT$distinct $columns FROM\n$t1_sql";
1965 0 0         $t1_sql =~ s/^/ /mg if($Debug);
1966 0           $t1_sql = $db->format_select_from_subselect($t1_sql);
1967              
1968 0           $sql =~ s/(\nFROM\n\s*)\S.+\s+t1\b/$1$t1_sql t1/;
1969              
1970 0           unshift(@$bind, @$t1_bind);
1971              
1972 0 0         if(@t1_bind_params)
1973             {
1974 0           unshift(@bind_params, @t1_bind_params);
1975             }
1976             }
1977             }
1978              
1979 0 0         if($return_sql)
1980             {
1981 0 0         $db->release_dbh if($dbh_retained);
1982 0 0         return wantarray ? ($sql, $bind) : $sql;
1983             }
1984              
1985 0           my $error;
1986              
1987             TRY:
1988             {
1989 0           local $@;
  0            
1990              
1991             eval
1992 0           {
1993 0           local $dbh->{'RaiseError'} = 1;
1994              
1995 0 0         $Debug && warn "$sql (", join(', ', @$bind), ")\n";
1996             # $meta->prepare_select_options (defunct)
1997 0 0         my $sth = $prepare_cached ? $dbh->prepare_cached($sql, undef, 3) :
    0          
1998             $dbh->prepare($sql) or die $dbh->errstr;
1999              
2000 0           $sth->{'RaiseError'} = 1;
2001              
2002 0 0         if(@bind_params)
2003             {
2004 0           my $i = 1;
2005              
2006 0           foreach my $value (@$bind)
2007             {
2008 0           $sth->bind_param($i, $value, $bind_params[$i - 1]);
2009 0           $i++;
2010             }
2011              
2012 0           $sth->execute;
2013             }
2014             else
2015             {
2016 0           $sth->execute(@$bind);
2017             }
2018              
2019 0           my %row;
2020              
2021 0           my $col_num = 1;
2022 0           my $table_num = 0;
2023              
2024 0 0         if($select)
2025             {
2026 0           foreach my $orig_item (@$select)
2027             {
2028 0           my($class, $table_num, $column);
2029              
2030 0 0         my $item = (ref $orig_item eq 'SCALAR') ? $$orig_item : $orig_item;
2031              
2032 0 0         if($item =~ s/\s+AS\s+(\w.+)$//i)
2033             {
2034 0           $column = $1;
2035             }
2036              
2037 0 0         if(index($item, '.') < 0)
    0          
    0          
2038             {
2039 0           $table_num = 0;
2040 0           $class = $classes[$table_num];
2041 0   0       $column ||= $item;
2042             }
2043             elsif($item =~ /^t(\d+)\.(.+)$/)
2044             {
2045 0           $table_num = $1 - 1;
2046 0           $class = $classes[$table_num];
2047 0   0       $column ||= $2;
2048             }
2049             elsif($item =~ /^(['"]?)([^.(]+)\1\.(['"]?)(.+)\3$/)
2050             {
2051 0           my $table = $2;
2052 0           $class = $classes{$table};
2053 0   0       $column ||= $4;
2054 0   0       my $table_num = $tn{$table} || $rel_tn{$table};
2055             }
2056             else
2057             {
2058 0           $table_num = 0;
2059 0           $class = $classes[$table_num];
2060 0   0       $column ||= $item;
2061             }
2062              
2063 0           $sth->bind_col($col_num++, \$row{$class,$table_num}{$column});
2064             }
2065             }
2066             else
2067             {
2068 0 0         if($direct_inject)
2069             {
2070 0   0       my $driver = $db->driver || 'unknown';
2071              
2072 0           foreach my $table (@tables)
2073             {
2074 0           my $class = $classes{$table};
2075 0           my $key_map = $di_keys{$class};
2076              
2077 0           foreach my $column (@{$methods{$table}})
  0            
2078             {
2079 0 0         if($key_map->{$column} eq $column)
2080             {
2081 0           $sth->bind_col($col_num++, \$row{$class,$table_num}{$column});
2082             }
2083             else # attribute uses a db-formatted key
2084             {
2085 0           $sth->bind_col($col_num++, \$row{$class,$table_num}{$key_map->{$column},$driver});
2086             }
2087             }
2088              
2089 0           $table_num++;
2090             }
2091             }
2092             else
2093             {
2094 0           foreach my $table (@tables)
2095             {
2096 0           my $class = $classes{$table};
2097              
2098 0           foreach my $column (@{$methods{$table}})
  0            
2099             {
2100 0           $sth->bind_col($col_num++, \$row{$class,$table_num}{$column});
2101             }
2102              
2103 0           $table_num++;
2104             }
2105             }
2106             }
2107              
2108 0 0         if($return_iterator)
2109             {
2110 0           $iterator = Rose::DB::Object::Iterator->new(active => 1);
2111              
2112 0           my $count = 0;
2113              
2114             # More trading of code duplication for performance: build custom
2115             # subroutines depending on how much work needs to be done for
2116             # each iteration.
2117              
2118 0 0         if($with_objects)
2119             {
2120             # Ug, we have to handle duplicate data due to "...to many" relationships
2121             # fetched via outer joins.
2122 0 0         if($handle_dups)# || $deep_joins)
2123             {
2124 0           my(@seen, %seen, @sub_objects);
2125              
2126             #my @pk_columns = $meta->primary_key_column_names;
2127 0           my $pk_columns = $meta->primary_key_column_names_or_aliases;
2128              
2129             # Get list of primary key columns for each sub-table
2130 0           my @sub_pk_columns;
2131              
2132 0           foreach my $i (1 .. $num_subtables)
2133             {
2134             #$sub_pk_columns[$i + 1] = [ $classes[$i]->meta->primary_key_column_names ];
2135 0           $sub_pk_columns[$i + 1] = $classes[$i]->meta->primary_key_column_names_or_aliases;
2136             }
2137              
2138 0           my($last_object, %subobjects, %parent_objects);
2139              
2140 0           weaken(my $witerator = $iterator);
2141              
2142             $iterator->_next_code(sub
2143             {
2144 0     0     my($self) = shift;
2145              
2146 0           my $object = 0;
2147 0           my $object_is_ready = 0;
2148              
2149 0           my(@objects, $error);
2150              
2151             TRY:
2152             {
2153 0           local $@;
  0            
2154              
2155             eval
2156 0           {
2157 0           ROW: for(;;)
2158             {
2159 0 0         last ROW unless($sth);
2160              
2161 0           while($sth->fetch)
2162             {
2163 0           my $pk = join(PK_JOIN, map { $row{$object_class,0}{$_} } @$pk_columns);
  0            
2164              
2165             # If this is a new main (t1) table row that we haven't seen before
2166 0 0         unless($seen[0]{$pk}++)
2167             {
2168             # First, finish building the last object, if it exists
2169 0 0         if($last_object)
2170             {
2171             #$Debug && warn "Finish $object_class $last_object->{'id'}\n";
2172              
2173 0 0         if($direct_inject)
2174             {
2175 0           while(my($ident, $parent) = each(%parent_objects))
2176             {
2177 0           while(my($method, $subobjects) = each(%{$subobjects{$ident}}))
  0            
2178             {
2179 0           $parent->{$method} = $subobjects;
2180             }
2181             }
2182             }
2183             else
2184             {
2185 0           while(my($ident, $parent) = each(%parent_objects))
2186             {
2187 0           local $parent->{STATE_LOADING()} = 1;
2188              
2189 0           while(my($method, $subobjects) = each(%{$subobjects{$ident}}))
  0            
2190             {
2191 0           $parent->$method($subobjects);
2192             }
2193             }
2194             }
2195              
2196 0           %subobjects = ();
2197 0           %parent_objects = ();
2198              
2199             # Add the object to the final list of objects that we'll return
2200 0           push(@objects, $last_object);
2201              
2202 0           $object_is_ready = 1;
2203             }
2204              
2205             #$Debug && warn "Make $object_class $pk\n";
2206              
2207             # Now, create the object from this new main table row
2208 0 0         if($direct_inject)
2209             {
2210 0           $object = bless { STATE_IN_DB() => 1, %{$row{$object_class,0}}, %object_args }, $object_class;
  0            
2211             }
2212             else
2213             {
2214 0           $object = $object_class->new(%object_args);
2215              
2216 0           local $object->{STATE_LOADING()} = 1;
2217 0           $object->init(%{$row{$object_class,0}});
  0            
2218 0           $object->{STATE_IN_DB()} = 1;
2219             }
2220              
2221 0           $last_object = $object; # This is the "last object" from now on
2222 0           @sub_objects = (); # The list of sub-objects is per-object
2223 0           splice(@seen, 1); # Sub-objects seen is also per-object,
2224             # so trim it, but leave the t1 table info
2225 0           %seen = (); # Wipe sub-object parent tracking.
2226             }
2227              
2228 0 0 0       $object ||= $last_object or die "Missing object for primary key '$pk'";
2229              
2230 0           my $map_record;
2231              
2232 0           foreach my $i (1 .. $num_subtables)
2233             {
2234 0           my $mapped_object_method = $mapped_object_methods[$i];
2235 0 0 0       next if(defined $mapped_object_method && !$mapped_object_method);
2236              
2237 0           my $class = $classes[$i];
2238 0           my $tn = $i + 1;
2239              
2240             # Null primary key columns are not allowed
2241 0           my $sub_pk = join(PK_JOIN, grep { defined } map { $row{$class,$i}{$_} } @{$sub_pk_columns[$tn]});
  0            
  0            
  0            
2242 0 0         next unless(length $sub_pk);
2243              
2244 0           my $subobject = $seen[$i]{$sub_pk};
2245              
2246 0 0         unless($subobject)
2247             {
2248             # Make sub-object
2249 0 0         if($direct_inject)
2250             {
2251 0           $subobject = bless { STATE_IN_DB() => 1, %{$row{$class,$i}}, %subobject_args }, $class;
  0            
2252             }
2253             else
2254             {
2255 0           $subobject = $class->new(%subobject_args);
2256 0           local $subobject->{STATE_LOADING()} = 1;
2257 0           $subobject->init(%{$row{$class,$i}});
  0            
2258 0           $subobject->{STATE_IN_DB()} = 1;
2259             }
2260              
2261 0           $seen[$i]{$sub_pk} = $subobject;
2262             }
2263              
2264             # If this object belongs to an attribute that can have more
2265             # than one object then just save it for later in the
2266             # per-object sub-objects list.
2267 0 0         if($has_dups[$i])
2268             {
2269 0 0         if($mapped_object_methods[$i])
2270             {
2271 0           $map_record = $subobject;
2272             }
2273             else
2274             {
2275 0 0         if($map_record)
2276             {
2277 0 0         my $method = $mapped_object_methods[$i - 1] or next;
2278              
2279 0 0         if($direct_inject)
2280             {
2281 0           $subobject->{$method} = $map_record;
2282             }
2283             else
2284             {
2285 0           local $subobject->{STATE_LOADING()} = 1;
2286 0           $subobject->$method($map_record);
2287             }
2288              
2289 0           $map_record = 0;
2290             }
2291              
2292 0 0         next if(defined $mapped_object_methods[$i]);
2293              
2294 0 0 0       if($has_dups[$i] && (my $bt = $belongs_to[$i]))
2295             {
2296             #$subobjects_belong_to[$i] = $#{$sub_objects[$bt]};
2297              
2298 0           my $parent_object = $sub_objects[$bt];
2299              
2300             # XXX: Special heavyweight subobject pairing in multi-many queries
2301 0 0 0       if($multi_many && ref $parent_object eq 'ARRAY' && @$parent_object > 1)
      0        
2302             {
2303 0           my $maps = $subobject_method_map[$i + 1][$bt];
2304 0           my %check;
2305              
2306 0           foreach my $map (@$maps)
2307             {
2308 0           my $subobject_method = $map->[1];
2309 0           $check{$subobject_method} = $subobject->$subobject_method();
2310             }
2311              
2312 0           PARENT: foreach my $check_parent (reverse @$parent_object)
2313             {
2314 0           foreach my $map (@$maps)
2315             {
2316 0           my $parent_method = $map->[0];
2317 0 0         next PARENT unless($check_parent->$parent_method() eq $check{$map->[1]});
2318             }
2319              
2320 0           $parent_object = $check_parent;
2321 0           last PARENT;
2322             }
2323             }
2324              
2325             # XXX: This relies on parent objects coming before child
2326             # objects in the list of tables in the FROM clause.
2327 0 0         $parent_object = $parent_object->[-1] #$parent_object->[$subobjects_belong_to[$i]]
2328             if(ref $parent_object eq 'ARRAY');
2329              
2330 0           my $method = $subobject_methods[$i];
2331              
2332 0           my $ident = refaddr $parent_object;
2333 0 0         next if($seen{$ident,$method}{$sub_pk}++);
2334 0           $parent_objects{$ident} = $parent_object;
2335 0           push(@{$subobjects{$ident}{$method}}, $subobject);
  0            
2336             }
2337             else
2338             {
2339 0           my $ident = refaddr $object;
2340 0           my $method = $subobject_methods[$i];
2341 0 0         next if($seen{$ident,$method}{$sub_pk}++);
2342 0           $parent_objects{$ident} = $object;
2343 0           push(@{$subobjects{$ident}{$method}}, $subobject);
  0            
2344             }
2345              
2346 0           push(@{$sub_objects[$i]}, $subobject);
  0            
2347             }
2348             }
2349             else # Otherwise, just assign it
2350             {
2351 0           $sub_objects[$i] = $subobject;
2352 0           my $parent_object;
2353              
2354 0 0         if(my $bt = $belongs_to[$i])
2355             {
2356 0           $parent_object = $sub_objects[$bt];
2357             # XXX: This relies on parent objects coming before
2358             # child objects in the list of tables in the FROM
2359             # clause.
2360 0 0         $parent_object = $parent_object->[-1] if(ref $parent_object eq 'ARRAY');
2361             }
2362             else
2363             {
2364 0           $parent_object = $object;
2365             }
2366              
2367 0           my $method = $subobject_methods[$i];
2368              
2369             # Only assign "... to one" values once
2370 0 0         next if($seen{refaddr $parent_object,$method}++);
2371              
2372 0 0         if($direct_inject)
2373             {
2374 0           $parent_object->{$method} = $subobject;
2375             }
2376             else
2377             {
2378 0           local $parent_object->{STATE_LOADING()} = 1;
2379 0           $parent_object->$method($subobject);
2380             }
2381             }
2382             }
2383              
2384 0 0         if($skip_first)
2385             {
2386 0 0         next ROW if($seen[0]{$pk} > 1);
2387 0 0         ++$count if($seen[0]{$pk} == 1);
2388 0 0         next ROW if($count <= $skip_first);
2389              
2390 0           $skip_first = 0;
2391 0           @objects = (); # Discard all skipped objects...
2392 0           $object_is_ready = 0; # ...so none are ready now
2393 0           next ROW;
2394             }
2395              
2396 0 0         if($object_is_ready)
2397             {
2398 0           $self->{'_count'}++;
2399 0           last ROW;
2400             }
2401              
2402 61     61   644 no warnings;
  61         164  
  61         26429  
2403 0 0 0       if($manual_limit && $self->{'_count'} == $manual_limit)
2404             {
2405 0           $self->finish;
2406 0           last ROW;
2407             }
2408             }
2409              
2410             # Handle the left-over "last object" that needs to be finished and
2411             # added to the final list of objects to return.
2412 0 0 0       if($last_object && !$object_is_ready)
2413             {
2414             #$Debug && warn "Finish straggler $object_class $last_object->{'id'}\n";
2415              
2416 0 0         if($direct_inject)
2417             {
2418 0           while(my($ident, $parent) = each(%parent_objects))
2419             {
2420 0           while(my($method, $subobjects) = each(%{$subobjects{$ident}}))
  0            
2421             {
2422 0           $parent->{$method} = $subobjects;
2423             }
2424             }
2425             }
2426             else
2427             {
2428 0           while(my($ident, $parent) = each(%parent_objects))
2429             {
2430 0           local $parent->{STATE_LOADING()} = 1;
2431              
2432 0           while(my($method, $subobjects) = each(%{$subobjects{$ident}}))
  0            
2433             {
2434 0           $parent->$method($subobjects);
2435             }
2436             }
2437             }
2438              
2439 0           push(@objects, $last_object);
2440              
2441             # Set everything up to return this object, then be done
2442 0           $last_object = undef;
2443 0           $self->{'_count'}++;
2444 0           $sth = undef;
2445 0           last ROW;
2446             }
2447              
2448 0           last ROW;
2449             }
2450             };
2451              
2452 0           $error = $@;
2453             }
2454              
2455 0 0         if($error)
2456             {
2457 0 0         $self->error(ref $error ? $error : "next() - $error");
2458 0           $class->handle_error($self);
2459 0           return undef;
2460             }
2461              
2462 0 0         @objects = () if($skip_first);
2463              
2464 0 0         if(@objects)
2465             {
2466 61     61   524 no warnings; # undef count okay
  61         191  
  61         419581  
2467 0 0 0       if($manual_limit && $self->{'_count'} == $manual_limit)
2468             {
2469 0           $self->total($self->{'_count'});
2470 0           $self->finish;
2471             }
2472              
2473             #$Debug && warn "Return $object_class $objects[-1]{'id'}\n";
2474 0           return shift(@objects);
2475             }
2476              
2477             #$Debug && warn "Return 0\n";
2478 0           return 0;
2479 0           });
2480              
2481             }
2482             else # no duplicate rows to handle
2483             {
2484             $iterator->_next_code(sub
2485             {
2486 0     0     my($self) = shift;
2487              
2488 0           my $object = 0;
2489              
2490 0           my $error;
2491              
2492             TRY:
2493             {
2494 0           local $@;
  0            
2495              
2496             eval
2497 0           {
2498 0           ROW: for(;;)
2499             {
2500 0 0         unless($sth->fetch)
2501             {
2502 0           return 0;
2503             }
2504              
2505 0 0 0       next ROW if($skip_first && ++$count <= $skip_first);
2506              
2507 0 0         if($direct_inject)
2508             {
2509 0           $object = bless { STATE_IN_DB() => 1, %{$row{$object_class,0}}, %object_args }, $object_class;
  0            
2510             }
2511             else
2512             {
2513 0           $object = $object_class->new(%object_args);
2514              
2515 0           local $object->{STATE_LOADING()} = 1;
2516 0           $object->init(%{$row{$object_class,0}});
  0            
2517 0           $object->{STATE_IN_DB()} = 1;
2518             }
2519              
2520 0           my @sub_objects;
2521              
2522 0 0         if($with_objects)
2523             {
2524 0           foreach my $i (1 .. $num_subtables)
2525             {
2526 0           my $method = $subobject_methods[$i];
2527 0           my $class = $classes[$i];
2528              
2529             # Skip undefined subobjects
2530 0 0         next unless(grep { defined } values %{$row{$class,$i}});
  0            
  0            
2531              
2532 0           my $subobject;
2533              
2534 0 0         if($direct_inject)
2535             {
2536 0           $subobject = bless { STATE_IN_DB() => 1, %{$row{$class,$i}}, %subobject_args }, $class;
  0            
2537             }
2538             else
2539             {
2540 0           $subobject = $class->new(%subobject_args);
2541 0           local $subobject->{STATE_LOADING()} = 1;
2542 0           $subobject->init(%{$row{$class,$i}});
  0            
2543 0           $subobject->{STATE_IN_DB()} = 1;
2544             }
2545              
2546 0           $sub_objects[$i] = $subobject;
2547              
2548 0 0         if($direct_inject)
2549             {
2550 0 0         if(my $bt = $belongs_to[$i])
2551             {
2552 0           $sub_objects[$bt]->{$method} = $subobject;
2553             }
2554             else
2555             {
2556 0           $object->{$method} = $subobject;
2557             }
2558             }
2559             else
2560             {
2561 0 0         if(my $bt = $belongs_to[$i])
2562             {
2563 0           local $sub_objects[$bt]->{STATE_LOADING()} = 1;
2564 0           $sub_objects[$bt]->$method($subobject);
2565             }
2566             else
2567             {
2568 0           local $object->{STATE_LOADING()} = 1;
2569 0           $object->$method($subobject);
2570             }
2571             }
2572             }
2573             }
2574              
2575 0           $skip_first = 0;
2576 0           $self->{'_count'}++;
2577 0           last ROW;
2578             }
2579             };
2580              
2581 0           $error = $@;
2582             }
2583              
2584 0 0         if($error)
2585             {
2586 0 0         $self->error(ref $error ? $error : "next() - $error");
2587 0           $class->handle_error($self);
2588 0           return undef;
2589             }
2590              
2591 0 0         return $skip_first ? undef : $object;
2592 0           });
2593             }
2594             }
2595             else # no sub-objects at all
2596             {
2597             $iterator->_next_code(sub
2598             {
2599 0     0     my($self) = shift;
2600              
2601 0           my $object = 0;
2602              
2603 0           my $error;
2604              
2605             TRY:
2606             {
2607 0           local $@;
  0            
2608              
2609             eval
2610 0           {
2611 0           ROW: for(;;)
2612             {
2613 0 0         unless($sth->fetch)
2614             {
2615             #$self->total($self->{'_count'});
2616 0           return 0;
2617             }
2618              
2619 0 0 0       next ROW if($skip_first && ++$count <= $skip_first);
2620              
2621 0 0         if($direct_inject)
2622             {
2623 0           $object = bless { STATE_IN_DB() => 1, %{$row{$object_class,0}}, %object_args }, $object_class;
  0            
2624             }
2625             else
2626             {
2627 0           $object = $object_class->new(%object_args);
2628              
2629 0           local $object->{STATE_LOADING()} = 1;
2630 0           $object->init(%{$row{$object_class,0}});
  0            
2631 0           $object->{STATE_IN_DB()} = 1;
2632             }
2633              
2634 0           $skip_first = 0;
2635 0           $self->{'_count'}++;
2636 0           last ROW;
2637             }
2638             };
2639              
2640 0           $error = $@;
2641             }
2642              
2643 0 0         if($error)
2644             {
2645 0 0         $self->error(ref $error ? $error : "next() - $error");
2646 0           $class->handle_error($self);
2647 0           return undef;
2648             }
2649              
2650 0           return $object;
2651 0           });
2652             }
2653              
2654             $iterator->_finish_code(sub
2655             {
2656 0 0   0     $sth->finish if($sth);
2657 0 0 0       $db->release_dbh if($db && $dbh_retained);
2658 0           $sth = undef;
2659 0           $db = undef;
2660 0           });
2661              
2662             $iterator->_destroy_code(sub
2663             {
2664 0 0 0 0     $db->release_dbh if($db && $dbh_retained);
2665 0           $sth = undef;
2666 0           $db = undef;
2667 0           });
2668              
2669 0           return $iterator;
2670             }
2671              
2672 0           $count = 0;
2673              
2674 0 0         if($with_objects)
2675             {
2676             # This "if" clause is a totally separate code path for handling
2677             # duplicates rows. I'm doing this for performance reasons.
2678 0 0         if($handle_dups)# || $deep_joins)
2679             {
2680 0           my(@seen, %seen, @sub_objects);
2681              
2682             #my @pk_columns = $meta->primary_key_column_names;
2683 0           my $pk_columns = $meta->primary_key_column_names_or_aliases;
2684              
2685             # Get list of primary key columns for each sub-table
2686 0           my @sub_pk_columns;
2687              
2688 0           foreach my $i (1 .. $num_subtables)
2689             {
2690             #$sub_pk_columns[$i + 1] = [ $classes[$i]->meta->primary_key_column_names ];
2691 0           $sub_pk_columns[$i + 1] = $classes[$i]->meta->primary_key_column_names_or_aliases;
2692             }
2693              
2694 0           my($last_object, %subobjects, %parent_objects);
2695              
2696 0           ROW: while($sth->fetch)
2697             {
2698 0           my $pk = join(PK_JOIN, map { $row{$object_class,0}{$_} } @$pk_columns);
  0            
2699              
2700 0           my $object;
2701              
2702             # If this is a new main (t1) table row that we haven't seen before
2703 0 0         unless($seen[0]{$pk}++)
2704             {
2705             # First, finish building the last object, if it exists
2706 0 0         if($last_object)
2707             {
2708 0 0         if($direct_inject)
2709             {
2710 0           while(my($ident, $parent) = each(%parent_objects))
2711             {
2712 0           while(my($method, $subobjects) = each(%{$subobjects{$ident}}))
  0            
2713             {
2714 0           $parent->{$method} = $subobjects; # XXX
2715             }
2716             }
2717             }
2718             else
2719             {
2720 0           while(my($ident, $parent) = each(%parent_objects))
2721             {
2722 0           local $parent->{STATE_LOADING()} = 1;
2723              
2724 0           while(my($method, $subobjects) = each(%{$subobjects{$ident}}))
  0            
2725             {
2726 0           $parent->$method($subobjects);
2727             }
2728             }
2729             }
2730              
2731 0           %subobjects = ();
2732 0           %parent_objects = ();
2733              
2734             # Add the object to the final list of objects that we'll return
2735 0           push(@objects, $last_object);
2736              
2737 0 0 0       if(!$skip_first && $manual_limit && @objects == $manual_limit)
      0        
2738             {
2739 0           last ROW;
2740             }
2741             }
2742              
2743             # Now, create the object from this new main table row
2744 0 0         if($direct_inject)
2745             {
2746 0           $object = bless { STATE_IN_DB() => 1, %{$row{$object_class,0}}, %object_args }, $object_class;
  0            
2747             }
2748             else
2749             {
2750 0           $object = $object_class->new(%object_args);
2751              
2752 0           local $object->{STATE_LOADING()} = 1;
2753 0           $object->init(%{$row{$object_class,0}});
  0            
2754 0           $object->{STATE_IN_DB()} = 1;
2755             }
2756              
2757 0           $last_object = $object; # This is the "last object" from now on.
2758 0           @sub_objects = (); # The list of sub-objects is per-object.
2759 0           splice(@seen, 1); # Sub-objects seen is also per-object,
2760             # so trim it, but leave the t1 table info.
2761 0           %seen = (); # Wipe sub-object parent tracking.
2762             }
2763              
2764 0 0 0       $object ||= $last_object or die "Missing object for primary key '$pk'";
2765              
2766 0           my $map_record;
2767              
2768 0           foreach my $i (1 .. $num_subtables)
2769             {
2770 0           my $mapped_object_method = $mapped_object_methods[$i];
2771 0 0 0       next if(defined $mapped_object_method && !$mapped_object_method);
2772              
2773 0           my $class = $classes[$i];
2774 0           my $tn = $i + 1;
2775              
2776             # Null primary key columns are not allowed
2777 0           my $sub_pk = join(PK_JOIN, grep { defined } map { $row{$class,$i}{$_} } @{$sub_pk_columns[$tn]});
  0            
  0            
  0            
2778 0 0         next unless(length $sub_pk);
2779              
2780 0           my $subobject = $seen[$i]{$sub_pk};
2781              
2782 0 0         unless($subobject)
2783             {
2784             # Make sub-object
2785 0 0         if($direct_inject)
2786             {
2787 0           $subobject = bless { STATE_IN_DB() => 1, %{$row{$class,$i}}, %subobject_args }, $class;
  0            
2788             }
2789             else
2790             {
2791 0           $subobject = $class->new(%subobject_args);
2792 0           local $subobject->{STATE_LOADING()} = 1;
2793 0           $subobject->init(%{$row{$class,$i}});
  0            
2794 0           $subobject->{STATE_IN_DB()} = 1;
2795             }
2796              
2797 0           $seen[$i]{$sub_pk} = $subobject;
2798             }
2799              
2800             # If this object belongs to an attribute that can have more
2801             # than one object then just save it for later in the
2802             # per-object sub-objects list.
2803 0 0         if($has_dups[$i])
2804             {
2805 0 0         if($mapped_object_method)
2806             {
2807 0           $map_record = $subobject;
2808             }
2809             else
2810             {
2811 0 0         if($map_record)
2812             {
2813 0 0         my $method = $mapped_object_methods[$i - 1] or next;
2814              
2815 0 0         if($direct_inject)
2816             {
2817 0           $subobject->{$method} = $map_record;
2818             }
2819             else
2820             {
2821 0           local $subobject->{STATE_LOADING()} = 1;
2822 0           $subobject->$method($map_record);
2823             }
2824              
2825 0           $map_record = 0;
2826             }
2827              
2828 0 0         next if(defined $mapped_object_methods[$i]);
2829              
2830 0 0 0       if($has_dups[$i] && (my $bt = $belongs_to[$i]))
2831             {
2832             #$subobjects_belong_to[$i] = $#{$sub_objects[$bt]};
2833              
2834 0           my $parent_object = $sub_objects[$bt];
2835              
2836             # XXX: Special heavyweight subobject pairing in multi-many queries
2837 0 0 0       if($multi_many && ref $parent_object eq 'ARRAY' && @$parent_object > 1)
      0        
2838             {
2839 0           my $maps = $subobject_method_map[$i + 1][$bt];
2840 0           my %check;
2841              
2842 0           foreach my $map (@$maps)
2843             {
2844 0           my $subobject_method = $map->[1];
2845 0           $check{$subobject_method} = $subobject->$subobject_method();
2846             }
2847              
2848 0           PARENT: foreach my $check_parent (reverse @$parent_object)
2849             {
2850 0           foreach my $map (@$maps)
2851             {
2852 0           my $parent_method = $map->[0];
2853 0 0         next PARENT unless($check_parent->$parent_method() eq $check{$map->[1]});
2854             }
2855              
2856 0           $parent_object = $check_parent;
2857 0           last PARENT;
2858             }
2859             }
2860              
2861             # XXX: This relies on parent objects coming before child
2862             # objects in the list of tables in the FROM clause.
2863 0 0         $parent_object = $parent_object->[-1] #$parent_object->[$subobjects_belong_to[$i]]
2864             if(ref $parent_object eq 'ARRAY');
2865              
2866 0           my $method = $subobject_methods[$i];
2867              
2868 0           my $ident = refaddr $parent_object;
2869 0 0         next if($seen{$ident,$method}{$sub_pk}++);
2870 0           $parent_objects{$ident} = $parent_object;
2871 0           push(@{$subobjects{$ident}{$method}}, $subobject);
  0            
2872             }
2873             else
2874             {
2875 0           my $ident = refaddr $object;
2876 0           my $method = $subobject_methods[$i];
2877 0 0         next if($seen{$ident,$method}{$sub_pk}++);
2878 0           $parent_objects{$ident} = $object;
2879 0           push(@{$subobjects{$ident}{$method}}, $subobject);
  0            
2880             }
2881              
2882 0           push(@{$sub_objects[$i]}, $subobject);
  0            
2883             }
2884             }
2885             else # Otherwise, just assign it
2886             {
2887 0           push(@{$sub_objects[$i]}, $subobject);
  0            
2888              
2889 0           my $parent_object;
2890              
2891 0 0         if(my $bt = $belongs_to[$i])
2892             {
2893 0           $parent_object = $sub_objects[$bt];
2894             # XXX: This relies on parent objects coming before child
2895             # objects in the list of tables in the FROM clause.
2896 0 0         $parent_object = $parent_object->[-1] if(ref $parent_object eq 'ARRAY');
2897             }
2898             else
2899             {
2900 0           $parent_object = $object;
2901             }
2902              
2903 0           my $method = $subobject_methods[$i];
2904              
2905             # Only assign "... to one" values once
2906 0 0         next if($seen{refaddr $parent_object,$method}++);
2907              
2908 0 0         if($direct_inject)
2909             {
2910 0           $parent_object->{$method} = $subobject;
2911             }
2912             else
2913             {
2914 0           local $parent_object->{STATE_LOADING()} = 1;
2915 0           $parent_object->$method($subobject);
2916             }
2917             }
2918             }
2919              
2920 0 0         if($skip_first)
2921             {
2922 0 0         next ROW if($seen[0]{$pk} > 1);
2923 0 0         next ROW if(@objects < $skip_first);
2924              
2925 0           $skip_first = 0;
2926 0           @objects = (); # Discard all skipped objects
2927 0           next ROW;
2928             }
2929             }
2930              
2931             # Handle the left-over "last object" that needs to be finished and
2932             # added to the final list of objects to return.
2933 0 0 0       if($last_object && !$skip_first)
2934             {
2935 0 0         if($direct_inject)
2936             {
2937 0           while(my($ident, $parent) = each(%parent_objects))
2938             {
2939 0           while(my($method, $subobjects) = each(%{$subobjects{$ident}}))
  0            
2940             {
2941 0           $parent->{$method} = $subobjects; # XXX
2942             }
2943             }
2944             }
2945             else
2946             {
2947 0           while(my($ident, $parent) = each(%parent_objects))
2948             {
2949 0           local $parent->{STATE_LOADING()} = 1;
2950              
2951 0           while(my($method, $subobjects) = each(%{$subobjects{$ident}}))
  0            
2952             {
2953 0           $parent->$method($subobjects);
2954             }
2955             }
2956             }
2957              
2958 0 0 0       unless($manual_limit && @objects >= $manual_limit)
2959             {
2960 0           push(@objects, $last_object);
2961             }
2962             }
2963              
2964 0 0         @objects = () if($skip_first);
2965             }
2966             else # simple sub-objects case: nothing worse than one-to-one relationships
2967             {
2968 0 0         if($skip_first)
2969             {
2970 0           while($sth->fetch)
2971             {
2972 0 0         next if(++$count < $skip_first);
2973 0           last;
2974             }
2975             }
2976              
2977 0           while($sth->fetch)
2978             {
2979 0           my $object;
2980              
2981 0 0         if($direct_inject)
2982             {
2983 0           $object = bless { STATE_IN_DB() => 1, %{$row{$object_class,0}}, %object_args }, $object_class;
  0            
2984             }
2985             else
2986             {
2987 0           $object = $object_class->new(%object_args);
2988              
2989 0           local $object->{STATE_LOADING()} = 1;
2990 0           $object->init(%{$row{$object_class,0}});
  0            
2991 0           $object->{STATE_IN_DB()} = 1;
2992             }
2993              
2994 0           my @sub_objects;
2995              
2996 0           foreach my $i (1 .. $num_subtables)
2997             {
2998 0           my $method = $subobject_methods[$i];
2999 0           my $class = $classes[$i];
3000              
3001             # Skip undefined subobjects
3002 0 0         next unless(grep { defined } values %{$row{$class,$i}});
  0            
  0            
3003              
3004 0           my $subobject;
3005              
3006 0 0         if($direct_inject)
3007             {
3008 0           $subobject = bless { STATE_IN_DB() => 1, %{$row{$class,$i}}, %subobject_args }, $class;
  0            
3009             }
3010             else
3011             {
3012 0           $subobject = $class->new(%subobject_args);
3013 0           local $subobject->{STATE_LOADING()} = 1;
3014 0           $subobject->init(%{$row{$class,$i}});
  0            
3015 0           $subobject->{STATE_IN_DB()} = 1;
3016             }
3017              
3018 0           $sub_objects[$i] = $subobject;
3019              
3020 0 0         if($direct_inject)
3021             {
3022 0 0         if(my $bt = $belongs_to[$i])
3023             {
3024 0           $sub_objects[$bt]->{$method} = $subobject;
3025             }
3026             else
3027             {
3028 0           $object->{$method} = $subobject;
3029             }
3030             }
3031             else
3032             {
3033 0 0         if(my $bt = $belongs_to[$i])
3034             {
3035 0           local $sub_objects[$bt]->{STATE_LOADING()} = 1;
3036 0           $sub_objects[$bt]->$method($subobject);
3037             }
3038             else
3039             {
3040 0           local $object->{STATE_LOADING()} = 1;
3041 0           $object->$method($subobject);
3042             }
3043             }
3044             }
3045              
3046 0           push(@objects, $object);
3047             }
3048             }
3049             }
3050             else # even simpler: no sub-objects at all
3051             {
3052 0 0         if($skip_first)
3053             {
3054 0           while($sth->fetch)
3055             {
3056 0 0         next if(++$count < $skip_first);
3057 0           last;
3058             }
3059             }
3060              
3061 0 0         if($direct_inject)
3062             {
3063 0           my $key_map = $di_keys{$object_class};
3064              
3065 0           while($sth->fetch)
3066             {
3067 0           push(@objects, bless { STATE_IN_DB() => 1, %{$row{$object_class,0}}, %object_args }, $object_class);
  0            
3068             }
3069             }
3070             else
3071             {
3072 0           while($sth->fetch)
3073             {
3074 0           my $object = $object_class->new(%object_args);
3075              
3076 0           local $object->{STATE_LOADING()} = 1;
3077 0           $object->init(%{$row{$object_class,0}});
  0            
3078 0           $object->{STATE_IN_DB()} = 1;
3079              
3080 0           push(@objects, $object);
3081             }
3082             }
3083             }
3084              
3085 0           $sth->finish;
3086             };
3087              
3088 0           $error = $@;
3089             }
3090              
3091 0 0         return $iterator if($iterator);
3092              
3093 0 0         $db->release_dbh if($dbh_retained);
3094              
3095 0 0         if($error)
3096             {
3097 0 0         $class->error(ref $error ? $error : "get_objects() - $error");
3098 0           $class->handle_error($class);
3099 0           return undef;
3100             }
3101              
3102 0           return \@objects;
3103             }
3104              
3105             sub _map_action
3106             {
3107 0     0     my($class, $action, @objects) = @_;
3108              
3109 0           $class->error(undef);
3110              
3111 0           foreach my $object (@objects)
3112             {
3113 0 0         unless($object->$action())
3114             {
3115 0           $class->error($object->error);
3116 0           $class->handle_error($class);
3117 0           return;
3118             }
3119             }
3120              
3121 0           return 1;
3122             }
3123              
3124 0     0 0   sub save_objects { shift->_map_action('save', @_) }
3125              
3126             sub delete_objects
3127             {
3128 0     0 1   my($class, %args);
3129              
3130 0 0         if(ref $_[1])
3131             {
3132 0           $class = shift;
3133              
3134 0 0         if(ref $_[0] eq 'HASH')
    0          
3135             {
3136 0           %args = (where => [ %{shift(@_)} ], @_);
  0            
3137             }
3138             elsif(ref $_[0] eq 'ARRAY')
3139             {
3140 0           %args = (where => shift, @_);
3141             }
3142 0           else { Carp::croak 'Invalid arguments: ', join(', ', @_) }
3143              
3144 0           unshift(@_, $class); # restore original args
3145             }
3146             else
3147             {
3148 0           ($class, %args) = @_;
3149             }
3150              
3151 0           $class->error(undef);
3152              
3153 0 0 0       my $object_class = $args{'object_class'} || $class->object_class
3154             or Carp::croak "Missing object class argument";
3155              
3156 0           my $meta = $object_class->meta;
3157              
3158             my $prepare_cached =
3159 0 0         exists $args{'prepare_cached'} ? $args{'prepare_cached'} :
3160             $class->dbi_prepare_cached;
3161              
3162 0   0       my $db = $args{'db'} ||= $object_class->init_db;
3163 0           my $dbh = $args{'dbh'};
3164 0           my $dbh_retained = 0;
3165              
3166 0 0         unless($dbh)
3167             {
3168 0 0         unless($dbh = $db->retain_dbh)
3169             {
3170 0           $class->error($db->error);
3171 0           $class->handle_error($class);
3172 0           return undef;
3173             }
3174              
3175 0           $args{'dbh'} = $dbh;
3176 0           $dbh_retained = 1;
3177             }
3178              
3179 0           $args{'query'} = delete $args{'where'};
3180              
3181 0 0 0       unless(($args{'query'} && @{$args{'query'}}) ||
  0   0        
      0        
      0        
3182 0           ($args{'clauses'} && @{$args{'clauses'}}) ||
3183             delete $args{'all'})
3184             {
3185 0           Carp::croak "$class - Refusing to delete all rows from the table '",
3186             $meta->fq_table($db), "' without an explict ",
3187             "'all => 1' parameter. (No 'where' or 'clauses' parameters ",
3188             "were passed to limit the scope of the delete operation.)";
3189             }
3190              
3191 0 0 0       if($args{'query'} && @{$args{'query'}} && $args{'all'})
  0   0        
3192             {
3193 0           Carp::croak "Illegal use of the 'where' and 'all' parameters in the same call";
3194             }
3195              
3196             # Yes, I'm re-using get_objects() code like crazy, and often
3197             # in weird ways. Shhhh, it's a secret.
3198              
3199 0           my @bind_params;
3200 0           $args{'bind_params'} = \@bind_params;
3201              
3202             # Avert your eyes...
3203 0           my($where, $bind) =
3204             $class->get_objects(%args, return_sql => 1, where_only => 1, table_aliases => undef);
3205              
3206 0 0         my $sql = 'DELETE FROM ' . $meta->fq_table_sql($db) .
3207             ($where ? " WHERE\n$where" : '');
3208              
3209 0           my($count, $error);
3210              
3211             TRY:
3212             {
3213 0           local $@;
  0            
3214              
3215             eval
3216 0           {
3217 0           local $dbh->{'RaiseError'} = 1;
3218 0 0         $Debug && warn "$sql - bind params: ", join(', ', @$bind), "\n";
3219              
3220             # $meta->prepare_bulk_delete_options (defunct)
3221 0 0         my $sth = $prepare_cached ? $dbh->prepare_cached($sql, undef, 3) :
    0          
3222             $dbh->prepare($sql) or die $dbh->errstr;
3223              
3224 0 0         if(@bind_params)
3225             {
3226 0           my $i = 1;
3227              
3228 0           foreach my $value (@$bind)
3229             {
3230 0           $sth->bind_param($i, $value, $bind_params[$i - 1]);
3231 0           $i++;
3232             }
3233              
3234 0           $sth->execute;
3235             }
3236             else
3237             {
3238 0           $sth->execute(@$bind);
3239             }
3240              
3241 0   0       $count = $sth->rows || 0;
3242             };
3243              
3244 0           $error = $@;
3245             }
3246              
3247 0 0         if($error)
3248             {
3249 0 0         $class->error(ref $error ? $error : "delete_objects() - $error");
3250 0           $class->handle_error($class);
3251 0           return undef;
3252             }
3253              
3254 0           return $count;
3255             }
3256              
3257             sub update_objects
3258             {
3259 0     0 1   my($class, %args) = @_;
3260              
3261 0           $class->error(undef);
3262              
3263 0 0 0       my $object_class = $args{'object_class'} || $class->object_class
3264             or Carp::croak "Missing object class argument";
3265              
3266 0           my $meta = $object_class->meta;
3267              
3268             my $prepare_cached =
3269 0 0         exists $args{'prepare_cached'} ? $args{'prepare_cached'} :
3270             $class->dbi_prepare_cached;
3271              
3272 0   0       my $db = $args{'db'} ||= $object_class->init_db;
3273 0           my $dbh = $args{'dbh'};
3274 0           my $dbh_retained = 0;
3275              
3276 0 0         unless($dbh)
3277             {
3278 0 0         unless($dbh = $db->retain_dbh)
3279             {
3280 0           $class->error($db->error);
3281 0           $class->handle_error($class);
3282 0           return undef;
3283             }
3284              
3285 0           $args{'dbh'} = $dbh;
3286 0           $dbh_retained = 1;
3287             }
3288              
3289 0 0 0       unless(($args{'where'} && @{$args{'where'}}) || delete $args{'all'})
  0   0        
3290             {
3291 0           Carp::croak "$class - Refusing to update all rows in the table '",
3292             $meta->fq_table_sql($db), "' without an explict ",
3293             "'all => 1' parameter";
3294             }
3295              
3296 0 0 0       if($args{'where'} && @{$args{'where'}} && $args{'all'})
  0   0        
3297             {
3298 0           Carp::croak "Illegal use of the 'where' and 'all' parameters in the same call";
3299             }
3300              
3301 0           my $where = delete $args{'where'};
3302 0 0         my $set = delete $args{'set'}
3303             or Carp::croak "Missing requires 'set' parameter";
3304              
3305 0 0         $set = [ %$set ] if(ref $set eq 'HASH');
3306              
3307             # Yes, I'm re-using get_objects() code like crazy, and often
3308             # in weird ways. Shhhh, it's a secret.
3309              
3310 0           my @bind_params;
3311 0           $args{'bind_params'} = \@bind_params;
3312              
3313 0           $args{'query'} = $set;
3314              
3315             # Avert your eyes...
3316 0           my($set_sql, $set_bind) =
3317             $class->get_objects(%args,
3318             return_sql => 1,
3319             where_only => 1,
3320             logic => ',',
3321             set => 1,
3322             table_aliases => 0);
3323              
3324 0           my $sql;
3325              
3326 0           my $where_bind = [];
3327              
3328 0 0         if($args{'query'} = $where)
3329             {
3330 0           my $where_sql;
3331              
3332 0           ($where_sql, $where_bind) =
3333             $class->get_objects(%args,
3334             return_sql => 1,
3335             where_only => 1,
3336             table_aliases => 0);
3337              
3338 0           $sql = 'UPDATE ' . $meta->fq_table_sql($db) .
3339             "\nSET\n$set_sql\nWHERE\n$where_sql";
3340             }
3341             else
3342             {
3343 0           $sql = 'UPDATE ' . $meta->fq_table_sql($db) . "\nSET\n$set_sql";
3344             }
3345              
3346 0           my($count, $error);
3347              
3348             TRY:
3349             {
3350 0           local $@;
  0            
3351              
3352             eval
3353 0           {
3354 0           local $dbh->{'RaiseError'} = 1;
3355 0 0         $Debug && warn "$sql (", join(', ', @$set_bind, @$where_bind), ")\n";
3356              
3357             # $meta->prepare_bulk_update_options (defunct)
3358 0 0         my $sth = $prepare_cached ? $dbh->prepare_cached($sql, undef, 3) :
    0          
3359             $dbh->prepare($sql) or die $dbh->errstr;
3360              
3361 0 0         if(@bind_params)
3362             {
3363 0           my $i = 1;
3364              
3365 0           foreach my $value (@$set_bind, @$where_bind)
3366             {
3367 0           $sth->bind_param($i, $value, $bind_params[$i - 1]);
3368 0           $i++;
3369             }
3370              
3371 0           $sth->execute;
3372             }
3373             else
3374             {
3375 0           $sth->execute(@$set_bind, @$where_bind);
3376             }
3377              
3378 0   0       $count = $sth->rows || 0;
3379             };
3380              
3381 0           $error = $@;
3382             }
3383              
3384 0 0         if($error)
3385             {
3386 0 0         $class->error(ref $error ? $error : "update_objects() - $error");
3387 0           $class->handle_error($class);
3388 0           return undef;
3389             }
3390              
3391 0           return $count;
3392             }
3393              
3394             sub make_manager_method_from_sql
3395             {
3396 0     0 1   my($class) = shift;
3397              
3398 0           my %args;
3399              
3400 0 0         if(@_ == 2)
3401             {
3402 0           %args = (method => $_[0], sql => $_[1]);
3403             }
3404 0           else { %args = @_ }
3405              
3406 0           my $named_args = delete $args{'params'};
3407              
3408 0 0         my $method = delete $args{'method'} or Carp::croak "Missing method name";
3409 0           my $code;
3410              
3411 0           $args{'_methods'} = {}; # Will fill in on first run
3412              
3413 0 0         my $worker_method = $args{'iterator'} ?
3414             'get_objects_iterator_from_sql' : 'get_objects_from_sql';
3415              
3416 0 0         if($named_args)
3417             {
3418 0           my @params = @$named_args; # every little bit counts
3419              
3420             $code = sub
3421             {
3422 0     0     my($self, %margs) = @_;
3423             $self->$worker_method(
3424             %args,
3425 0           args => [ delete @margs{@params} ],
3426             %margs);
3427 0           };
3428             }
3429             else
3430             {
3431 0     0     $code = sub { shift->$worker_method(%args, args => \@_) };
  0            
3432             }
3433              
3434 61     61   690 no strict 'refs';
  61         226  
  61         99652  
3435 0           *{"${class}::$method"} = $code;
  0            
3436              
3437 0           return $code;
3438             }
3439              
3440             sub get_objects_from_sql
3441             {
3442 0     0 1   my($class) = shift;
3443              
3444 0           my(%args, $sql);
3445              
3446 0 0         if(@_ == 1) { $sql = shift }
  0            
3447             else
3448             {
3449 0           %args = @_;
3450 0           $sql = $args{'sql'};
3451             }
3452              
3453 0 0         Carp::croak "Missing SQL" unless($sql);
3454              
3455 0   0       my $object_class = $args{'object_class'} || $class->object_class ||
3456             Carp::croak "Missing object class";
3457              
3458 0 0         my $meta = $object_class->meta
3459             or Carp::croak "Could not get meta for $object_class";
3460              
3461             my $prepare_cached =
3462 0 0         exists $args{'prepare_cached'} ? $args{'prepare_cached'} :
3463             $class->dbi_prepare_cached;
3464              
3465 0           my $methods = $args{'_methods'};
3466 0   0       my $exec_args = $args{'args'} || [];
3467 0           my $attr = $args{'prepare_options'};
3468              
3469 0 0 0       my $have_methods = ($args{'_methods'} && %{$args{'_methods'}}) ? 1 : 0;
3470              
3471 0   0       my $db = delete $args{'db'} || $object_class->init_db;
3472 0           my $dbh = delete $args{'dbh'};
3473 0           my $dbh_retained = 0;
3474              
3475 0 0         unless($dbh)
3476             {
3477 0 0         unless($dbh = $db->retain_dbh)
3478             {
3479 0           $class->error($db->error);
3480 0           $class->handle_error($class);
3481 0           return undef;
3482             }
3483              
3484 0           $dbh_retained = 1;
3485             }
3486              
3487             my %object_args =
3488             (
3489 0 0         (exists $args{'share_db'} ? $args{'share_db'} : 1) ? (db => $db) : ()
    0          
3490             );
3491              
3492 0           my(@objects, $error);
3493              
3494             TRY:
3495             {
3496 0           local $@;
  0            
3497              
3498             eval
3499 0           {
3500 0           local $dbh->{'RaiseError'} = 1;
3501              
3502 0 0         $Debug && warn "$sql (", join(', ', @$exec_args), ")\n";
3503 0 0         my $sth = $prepare_cached ? $dbh->prepare_cached($sql, $attr, 3) :
    0          
3504             $dbh->prepare($sql, $attr) or die $dbh->errstr;
3505              
3506 0           $sth->execute(@$exec_args);
3507              
3508 0           while(my $row = $sth->fetchrow_hashref)
3509             {
3510 0 0         unless($have_methods)
3511             {
3512 0           foreach my $col (keys %$row)
3513             {
3514 0 0         if($meta->column($col))
    0          
    0          
    0          
3515             {
3516 0           $methods->{$col} = $meta->column_mutator_method_name($col);
3517             }
3518             elsif($object_class->can($col))
3519             {
3520 0           $methods->{$col} = $col;
3521             }
3522             elsif($meta->column(lc $col))
3523             {
3524 0           $methods->{$col} = $meta->column_mutator_method_name(lc $col);
3525             }
3526             elsif($object_class->can(lc $col))
3527             {
3528 0           $methods->{$col} = lc $col;
3529             }
3530             }
3531              
3532 0           $have_methods = 1;
3533             }
3534              
3535 0           my $object = $object_class->new(%object_args);
3536              
3537 0           local $object->{STATE_LOADING()} = 1;
3538 0           $object->{STATE_IN_DB()} = 1;
3539              
3540 0           while(my($col, $val) = each(%$row))
3541             {
3542 0   0       my $method = $methods->{$col} || $col;
3543 0           $object->$method($val);
3544             }
3545              
3546 0           $object->{MODIFIED_COLUMNS()} = {};
3547              
3548 0           push(@objects, $object);
3549             }
3550             };
3551              
3552 0           $error = $@;
3553             }
3554              
3555 0 0         $db->release_dbh if($dbh_retained);
3556              
3557 0 0         if($error)
3558             {
3559 0           $class->total(undef);
3560 0 0         $class->error(ref $error ? $error : "get_objects_from_sql() - $error");
3561 0           $class->handle_error($class);
3562 0           return undef;
3563             }
3564              
3565 0           return \@objects;
3566             }
3567              
3568             sub get_objects_iterator_from_sql
3569             {
3570 0     0 1   my($class) = shift;
3571              
3572 0           my(%args, $sql);
3573              
3574 0 0         if(@_ == 1) { $sql = shift }
  0            
3575             else
3576             {
3577 0           %args = @_;
3578 0           $sql = $args{'sql'};
3579             }
3580              
3581 0 0         Carp::croak "Missing SQL" unless($sql);
3582              
3583 0   0       my $object_class = $args{'object_class'} || $class->object_class ||
3584             Carp::croak "Missing object class";
3585              
3586 0   0       weaken(my $meta = $object_class->meta
3587             or Carp::croak "Could not get meta for $object_class");
3588              
3589             my $prepare_cached =
3590 0 0         exists $args{'prepare_cached'} ? $args{'prepare_cached'} :
3591             $class->dbi_prepare_cached;
3592              
3593 0           my $methods = $args{'_methods'};
3594 0   0       my $exec_args = $args{'args'} || [];
3595 0           my $attr = $args{'prepare_options'};
3596              
3597 0 0 0       my $have_methods = ($args{'_methods'} && %{$args{'_methods'}}) ? 1 : 0;
3598              
3599 0   0       my $db = delete $args{'db'} || $object_class->init_db;
3600 0           my $dbh = delete $args{'dbh'};
3601 0           my $dbh_retained = 0;
3602              
3603 0 0         unless($dbh)
3604             {
3605 0 0         unless($dbh = $db->retain_dbh)
3606             {
3607 0           $class->error($db->error);
3608 0           $class->handle_error($class);
3609 0           return undef;
3610             }
3611              
3612 0           $dbh_retained = 1;
3613             }
3614              
3615             my %object_args =
3616             (
3617 0 0         (exists $args{'share_db'} ? $args{'share_db'} : 1) ? (db => $db) : ()
    0          
3618             );
3619              
3620 0           my($sth, $error);
3621              
3622             TRY:
3623             {
3624 0           local $@;
  0            
3625              
3626             eval
3627 0           {
3628 0           local $dbh->{'RaiseError'} = 1;
3629              
3630 0 0         $Debug && warn "$sql (", join(', ', @$exec_args), ")\n";
3631 0 0         $sth = $prepare_cached ? $dbh->prepare_cached($sql, $attr, 3) :
    0          
3632             $dbh->prepare($sql, $attr) or die $dbh->errstr;
3633              
3634 0           $sth->execute(@$exec_args);
3635             };
3636              
3637 0           $error = $@;
3638             }
3639              
3640 0 0         if($error)
3641             {
3642 0 0         $db->release_dbh if($dbh_retained);
3643 0           $class->total(undef);
3644 0 0         $class->error(ref $error ? $error : "get_objects_iterator_from_sql() - $error");
3645 0           $class->handle_error($class);
3646 0           return undef;
3647             }
3648              
3649 0           my $iterator = Rose::DB::Object::Iterator->new(active => 1);
3650              
3651             $iterator->_next_code(sub
3652             {
3653 0     0     my($self) = shift;
3654              
3655 0           my $object = 0;
3656              
3657 0           my $error;
3658              
3659             TRY:
3660             {
3661 0           local $@;
  0            
3662              
3663             eval
3664 0           {
3665 0           ROW: for(;;)
3666             {
3667 0 0         my $row = $sth->fetchrow_hashref or return 0;
3668              
3669 0 0         unless($have_methods)
3670             {
3671 0           foreach my $col (keys %$row)
3672             {
3673 0 0         if($meta->column($col))
    0          
    0          
    0          
3674             {
3675 0           $methods->{$col} = $meta->column_mutator_method_name($col);
3676             }
3677             elsif($object_class->can($col))
3678             {
3679 0           $methods->{$col} = $col;
3680             }
3681             elsif($meta->column(lc $col))
3682             {
3683 0           $methods->{$col} = $meta->column_mutator_method_name(lc $col);
3684             }
3685             elsif($object_class->can(lc $col))
3686             {
3687 0           $methods->{$col} = lc $col;
3688             }
3689             }
3690              
3691 0           $have_methods = 1;
3692             }
3693              
3694 0           $object = $object_class->new(%object_args);
3695              
3696 0           local $object->{STATE_LOADING()} = 1;
3697 0           $object->{STATE_IN_DB()} = 1;
3698              
3699 0           while(my($col, $val) = each(%$row))
3700             {
3701 0           my $method = $methods->{$col};
3702 0           $object->$method($val);
3703             }
3704              
3705 0           $object->{MODIFIED_COLUMNS()} = {};
3706              
3707 0           $self->{'_count'}++;
3708 0           last ROW;
3709             }
3710             };
3711              
3712 0           $error = $@;
3713             }
3714              
3715 0 0         if($error)
3716             {
3717 0 0         $self->error(ref $error ? $error : "next() - $error");
3718 0           $class->handle_error($self);
3719 0           return undef;
3720             }
3721              
3722 0           return $object;
3723 0           });
3724              
3725             $iterator->_finish_code(sub
3726             {
3727 0 0   0     $sth->finish if($sth);
3728 0 0 0       $db->release_dbh if($db && $dbh_retained);
3729 0           $sth = undef;
3730 0           $db = undef;
3731 0           });
3732              
3733             $iterator->_destroy_code(sub
3734             {
3735 0 0 0 0     $db->release_dbh if($db && $dbh_retained);
3736 0           $sth = undef;
3737 0           $db = undef;
3738 0           });
3739              
3740 0           return $iterator;
3741             }
3742              
3743             sub perl_class_definition
3744             {
3745 0     0 1   my($class) = shift;
3746              
3747 0   0       my $object_class = $class->object_class || $class->_object_class;
3748              
3749 61     61   644 no strict 'refs';
  61         185  
  61         10982  
3750 0           my @isa = @{"${class}::ISA"};
  0            
3751              
3752 0           my $use_bases = "use base qw(@isa);";
3753              
3754 0           return<<"EOF";
3755             package $class;
3756              
3757             use strict;
3758              
3759             $use_bases
3760              
3761             use $object_class;
3762              
3763 0   0       sub object_class { '@{[ $class->object_class || $class->_object_class ]}' }
3764              
3765 0           __PACKAGE__->make_manager_methods('@{[ $class->_base_name ]}');
3766              
3767             1;
3768             EOF
3769             }
3770              
3771             1;
3772              
3773             __END__
3774              
3775             =head1 NAME
3776              
3777             Rose::DB::Object::Manager - Fetch multiple Rose::DB::Object-derived objects from the database using complex queries.
3778              
3779             =head1 SYNOPSIS
3780              
3781             ##
3782             ## Given the following Rose::DB::Object-derived classes...
3783             ##
3784              
3785             package Category;
3786              
3787             use base 'Rose::DB::Object';
3788              
3789             __PACKAGE__->meta->setup
3790             (
3791             table => 'categories',
3792             columns =>
3793             [
3794             id => { type => 'int', primary_key => 1 },
3795             name => { type => 'varchar', length => 255 },
3796             description => { type => 'text' },
3797             ],
3798              
3799             unique_key => 'name',
3800             );
3801              
3802             ...
3803              
3804             package CodeName;
3805              
3806             use base 'Rose::DB::Object';
3807              
3808             __PACKAGE__->meta->setup
3809             (
3810             table => 'code_names',
3811             columns =>
3812             [
3813             id => { type => 'int', primary_key => 1 },
3814             product_id => { type => 'int' },
3815             name => { type => 'varchar', length => 255 },
3816             applied => { type => 'date', not_null => 1 },
3817             ],
3818              
3819             foreign_keys =>
3820             [
3821             product =>
3822             {
3823             class => 'Product',
3824             key_columns => { product_id => 'id' },
3825             },
3826             ],
3827             );
3828              
3829             ...
3830              
3831             package Product;
3832              
3833             use base 'Rose::DB::Object';
3834              
3835             __PACKAGE__->meta->setup
3836             (
3837             table => 'products',
3838             columns =>
3839             [
3840             id => { type => 'int', primary_key => 1 },
3841             name => { type => 'varchar', length => 255 },
3842             description => { type => 'text' },
3843             category_id => { type => 'int' },
3844             region_num => { type => 'int' },
3845              
3846             status =>
3847             {
3848             type => 'varchar',
3849             check_in => [ 'active', 'inactive' ],
3850             default => 'inactive',
3851             },
3852              
3853             start_date => { type => 'datetime' },
3854             end_date => { type => 'datetime' },
3855              
3856             date_created => { type => 'timestamp', default => 'now' },
3857             last_modified => { type => 'timestamp', default => 'now' },
3858             ],
3859              
3860             unique_key => 'name',
3861              
3862             foreign_keys =>
3863             [
3864             category =>
3865             {
3866             class => 'Category',
3867             key_columns =>
3868             {
3869             category_id => 'id',
3870             }
3871             },
3872             ],
3873              
3874             relationships =>
3875             [
3876             code_names =>
3877             {
3878             type => 'one to many',
3879             class => 'CodeName',
3880             column_map => { id => 'product_id' },
3881             manager_args =>
3882             {
3883             sort_by => CodeName->meta->table . '.applied DESC',
3884             },
3885             },
3886             ],
3887             );
3888              
3889             ...
3890              
3891             ##
3892             ## Create a manager class
3893             ##
3894              
3895             package Product::Manager;
3896              
3897             use base 'Rose::DB::Object::Manager';
3898              
3899             sub object_class { 'Product' }
3900              
3901             __PACKAGE__->make_manager_methods('products');
3902              
3903             # The call above creates the methods shown below. (The actual
3904             # method bodies vary slightly, but this is the gist of it...)
3905             #
3906             # sub get_products
3907             # {
3908             # shift->get_objects(@_, object_class => 'Product');
3909             # }
3910             #
3911             # sub get_products_iterator
3912             # {
3913             # shift->get_objects_iterator(@_, object_class => 'Product');
3914             # }
3915             #
3916             # sub get_products_count
3917             # {
3918             # shift->get_objects_count(@_, object_class => 'Product');
3919             # }
3920             #
3921             # sub update_products
3922             # {
3923             # shift->update_objects(@_, object_class => 'Product');
3924             # }
3925             #
3926             # sub delete_products
3927             # {
3928             # shift->delete_objects(@_, object_class => 'Product');
3929             # }
3930              
3931             ...
3932              
3933             ##
3934             ## Use the manager class
3935             ##
3936              
3937             #
3938             # Get a reference to an array of objects
3939             #
3940              
3941             $products =
3942             Product::Manager->get_products
3943             (
3944             query =>
3945             [
3946             category_id => [ 5, 7, 22 ],
3947             status => 'active',
3948             start_date => { lt => '15/12/2005 6:30 p.m.' },
3949             name => { like => [ '%foo%', '%bar%' ] },
3950             ],
3951             sort_by => 'category_id, start_date DESC',
3952             limit => 100,
3953             offset => 80,
3954             );
3955              
3956             foreach my $product (@$products)
3957             {
3958             print $product->id, ' ', $product->name, "\n";
3959             }
3960              
3961             #
3962             # Get objects iterator
3963             #
3964              
3965             $iterator =
3966             Product::Manager->get_products_iterator
3967             (
3968             query =>
3969             [
3970             category_id => [ 5, 7, 22 ],
3971             status => 'active',
3972             start_date => { lt => '15/12/2005 6:30 p.m.' },
3973             name => { like => [ '%foo%', '%bar%' ] },
3974             ],
3975             sort_by => 'category_id, start_date DESC',
3976             limit => 100,
3977             offset => 80,
3978             );
3979              
3980             while($product = $iterator->next)
3981             {
3982             print $product->id, ' ', $product->name, "\n";
3983             }
3984              
3985             print $iterator->total;
3986              
3987             #
3988             # Get objects count
3989             #
3990              
3991             $count =
3992             Product::Manager->get_products_count
3993             (
3994             query =>
3995             [
3996             category_id => [ 5, 7, 22 ],
3997             status => 'active',
3998             start_date => { lt => '15/12/2005 6:30 p.m.' },
3999             name => { like => [ '%foo%', '%bar%' ] },
4000             ],
4001             );
4002              
4003             die Product::Manager->error unless(defined $count);
4004              
4005             print $count; # or Product::Manager->total()
4006              
4007             #
4008             # Get objects and sub-objects in a single query
4009             #
4010              
4011             $products =
4012             Product::Manager->get_products
4013             (
4014             with_objects => [ 'category', 'code_names' ],
4015             query =>
4016             [
4017             category_id => [ 5, 7, 22 ],
4018             status => 'active',
4019             start_date => { lt => '15/12/2005 6:30 p.m.' },
4020              
4021             # We need to disambiguate the "name" column below since it
4022             # appears in more than one table referenced by this query.
4023             # When more than one table is queried, the tables have numbered
4024             # aliases starting from the "main" table ("products"). The
4025             # "products" table is t1, "categories" is t2, and "code_names"
4026             # is t3. You can read more about automatic table aliasing in
4027             # the documentation for the get_objects() method below.
4028             #
4029             # "category.name" and "categories.name" would work too, since
4030             # table and relationship names are also valid prefixes.
4031              
4032             't2.name' => { like => [ '%foo%', '%bar%' ] },
4033             ],
4034             sort_by => 'category_id, start_date DESC',
4035             limit => 100,
4036             offset => 80,
4037             );
4038              
4039             foreach my $product (@$products)
4040             {
4041             # The call to $product->category does not hit the database
4042             print $product->name, ': ', $product->category->name, "\n";
4043              
4044             # The call to $product->code_names does not hit the database
4045             foreach my $code_name ($product->code_names)
4046             {
4047             # This call doesn't hit the database either
4048             print $code_name->name, "\n";
4049             }
4050             }
4051              
4052             #
4053             # Update objects
4054             #
4055              
4056             $num_rows_updated =
4057             Product::Manager->update_products(
4058             set =>
4059             {
4060             end_date => DateTime->now,
4061             region_num => { sql => 'region_num * -1' }
4062             status => 'defunct',
4063             },
4064             where =>
4065             [
4066             start_date => { lt => '1/1/1980' },
4067             status => [ 'active', 'pending' ],
4068             ]);
4069              
4070             #
4071             # Delete objects
4072             #
4073              
4074             $num_rows_deleted =
4075             Product::Manager->delete_products(
4076             where =>
4077             [
4078             status => [ 'stale', 'old' ],
4079             name => { like => 'Wax%' },
4080             or =>
4081             [
4082             start_date => { gt => '2008-12-30' },
4083             end_date => { gt => 'now' },
4084             ],
4085             ]);
4086              
4087             =head1 DESCRIPTION
4088              
4089             L<Rose::DB::Object::Manager> is a base class for classes that select rows from tables fronted by L<Rose::DB::Object>-derived classes. Each row in the table(s) queried is converted into the equivalent L<Rose::DB::Object>-derived object.
4090              
4091             Class methods are provided for fetching objects all at once, one at a time through the use of an iterator, or just getting the object count. Subclasses are expected to create syntactically pleasing wrappers for L<Rose::DB::Object::Manager> class methods, either manually or with the L<make_manager_methods|/make_manager_methods> method. A very minimal example is shown in the L<synopsis|/SYNOPSIS> above.
4092              
4093             =head1 CLASS METHODS
4094              
4095             =over 4
4096              
4097             =item B<dbi_prepare_cached [BOOL]>
4098              
4099             Get or set a boolean value that indicates whether or not this class will use L<DBI>'s L<prepare_cached|DBI/prepare_cached> method by default (instead of the L<prepare|DBI/prepare> method) when preparing SQL queries. The default value is false.
4100              
4101             =item B<default_limit_with_subselect [BOOL]>
4102              
4103             Get or set a boolean value that determines whether or not this class will consider using a sub-query to express C<limit>/C<offset> constraints when fetching sub-objects related through one of the "...-to-many" relationship types. Not all databases support this syntax, and not all queries can use it even in supported databases. If this parameter is true, the feature will be used when possible, by default. The default value is true.
4104              
4105             =item B<default_manager_method_types [ LIST | ARRAYREF ]>
4106              
4107             Get or set the default list of method types used by the L<make_manager_methods|/make_manager_methods> method. The default list is C<objects>, C<iterator>, C<count>, C<delete>, and C<update>.
4108              
4109             =item B<default_nested_joins [BOOL]>
4110              
4111             Get or set a boolean value that determines whether or not this class will consider using nested JOIN syntax when fetching related objects. Not all databases support this syntax, and not all queries can use it even in supported databases. If this parameter is true, the feature will be used when possible, by default. The default value is true.
4112              
4113             =item B<default_objects_per_page [NUM]>
4114              
4115             Get or set the default number of items per page, as returned by the L<get_objects|/get_objects> method when used with the C<page> and/or C<per_page> parameters. The default value is 20.
4116              
4117             =item B<delete_objects [ PARAMS | ARRAYREF | HASHREF ]>
4118              
4119             Delete rows from a table fronted by a L<Rose::DB::Object>-derived class based on PARAMS, where PARAMS are name/value pairs. Returns the number of rows deleted, or undef if there was an error.
4120              
4121             If the first argument is a reference to a hash or array, it is converted to a reference to an array (if necessary) and taken as the value of the C<where> parameter.
4122              
4123             Valid parameters are:
4124              
4125             =over 4
4126              
4127             =item B<all BOOL>
4128              
4129             If set to a true value, this parameter indicates an explicit request to delete all rows from the table. If both the C<all> and the C<where> parameters are passed, a fatal error will occur.
4130              
4131             =item B<db DB>
4132              
4133             A L<Rose::DB>-derived object used to access the database. If omitted, one will be created by calling the L<init_db|Rose::DB::Object/init_db> method of the L<object_class|/object_class>.
4134              
4135             =item B<prepare_cached BOOL>
4136              
4137             If true, then L<DBI>'s L<prepare_cached|DBI/prepare_cached> method will be used (instead of the L<prepare|DBI/prepare> method) when preparing the SQL statement that will delete the objects. If omitted, the default value is determined by the L<dbi_prepare_cached|/dbi_prepare_cached> class method.
4138              
4139             =item B<object_class CLASS>
4140              
4141             The name of the L<Rose::DB::Object>-derived class that fronts the table from which rows are to be deleted. This parameter is required; a fatal error will occur if it is omitted. Defaults to the value returned by the L<object_class|/object_class> class method.
4142              
4143             =item B<where ARRAYREF>
4144              
4145             The query parameters, passed as a reference to an array of name/value pairs. These pairs are used to formulate the "where" clause of the SQL query that is used to delete the rows from the table. Arbitrarily nested boolean logic is supported.
4146              
4147             For the complete list of valid parameter names and values, see the documentation for the C<query> parameter of the L<build_select|Rose::DB::Object::QueryBuilder/build_select> function in the L<Rose::DB::Object::QueryBuilder> module.
4148              
4149             If this parameter is omitted, this method will refuse to delete all rows from the table and a fatal error will occur. To delete all rows from a table, you must pass the C<all> parameter with a true value. If both the C<all> and the C<where> parameters are passed, a fatal error will occur.
4150              
4151             =back
4152              
4153             =item B<error>
4154              
4155             Returns the text message associated with the last error, or false if there was no error.
4156              
4157             =item B<error_mode [MODE]>
4158              
4159             Get or set the error mode for this class. The error mode determines what happens when a method of this class encounters an error. The default setting is "fatal", which means that methods will L<croak|Carp/croak> if they encounter an error.
4160              
4161             B<PLEASE NOTE:> The error return values described in the method documentation in the rest of this document are only relevant when the error mode is set to something "non-fatal." In other words, if an error occurs, you'll never see any of those return values if the selected error mode L<die|perlfunc/die>s or L<croak|Carp/croak>s or otherwise throws an exception when an error occurs.
4162              
4163             Valid values of MODE are:
4164              
4165             =over 4
4166              
4167             =item carp
4168              
4169             Call L<Carp::carp|Carp/carp> with the value of the object L<error|Rose::DB::Object/error> as an argument.
4170              
4171             =item cluck
4172              
4173             Call L<Carp::cluck|Carp/cluck> with the value of the object L<error|Rose::DB::Object/error> as an argument.
4174              
4175             =item confess
4176              
4177             Call L<Carp::confess|Carp/confess> with the value of the object L<error|Rose::DB::Object/error> as an argument.
4178              
4179             =item croak
4180              
4181             Call L<Carp::croak|Carp/croak> with the value of the object L<error|Rose::DB::Object/error> as an argument.
4182              
4183             =item fatal
4184              
4185             An alias for the "croak" mode.
4186              
4187             =item return
4188              
4189             Return a value that indicates that an error has occurred, as described in the documentation for each method.
4190              
4191             =back
4192              
4193             In all cases, the class's C<error> attribute will also contain the error message.
4194              
4195             =item B<get_objects [ PARAMS | HASHREF | ARRAYREF ]>
4196              
4197             Get L<Rose::DB::Object>-derived objects based on PARAMS, where PARAMS are name/value pairs. Returns a reference to a (possibly empty) array, or undef if there was an error.
4198              
4199             If the first argument is a reference to a hash or array, it is converted to a reference to an array (if necessary) and taken as the value of the C<query> parameter.
4200              
4201             Each table that participates in the query will be aliased. Each alias is in the form "tN" where "N" is an ascending number starting with 1. The tables are numbered as follows.
4202              
4203             =over 4
4204              
4205             =item * The primary table is always "t1"
4206              
4207             =item * The table(s) that correspond to each relationship or foreign key named in the C<with_objects> parameter are numbered in order, starting with "t2"
4208              
4209             =item * The table(s) that correspond to each relationship or foreign key named in the C<require_objects> parameter are numbered in order, starting where the C<with_objects> table aliases left off.
4210              
4211             =back
4212              
4213             "Many to many" relationships have two corresponding tables, and therefore will use two "tN" numbers. All other supported of relationship types only have just one table and will therefore use a single "tN" number.
4214              
4215             For example, imagine that the C<Product> class shown in the L<synopsis|/SYNOPSIS> also has a "many to many" relationship named "colors." Now consider this call:
4216              
4217             $products =
4218             Product::Manager->get_products(
4219             require_objects => [ 'category' ],
4220             with_objects => [ 'code_names', 'colors' ],
4221             multi_many_ok => 1,
4222             query => [ status => 'defunct' ],
4223             sort_by => 't1.name');
4224              
4225             The "products" table is "t1" since it's the primary table--the table behind the C<Product> class that C<Product::Manager> manages. Next, the C<with_objects> tables are aliased. The "code_names" table is "t2". Since "colors" is a "many to many" relationship, it gets two numbers: "t3" and "t4". Finally, the C<require_objects> tables are numbered: the table behind the foreign key "category" is "t5". Here's an annotated version of the example above:
4226              
4227             # Table aliases in the comments
4228             $products =
4229             Product::Manager->get_products(
4230             # t5
4231             require_objects => [ 'category' ],
4232             # t2 t3, t4
4233             with_objects => [ 'code_names', 'colors' ],
4234             multi_many_ok => 1,
4235             query => [ status => 'defunct' ],
4236             sort_by => 't1.name'); # "products" is "t1"
4237              
4238             Also note that the C<multi_many_ok> parameter was used in order to suppress the warning that occurs when more than one "... to many" relationship is included in the combination of C<require_objects> and C<with_objects> ("code_names" (one to many) and "colors" (many to many) in this case). See the documentation for C<multi_many_ok> below.
4239              
4240             The "tN" table aliases are for convenience, and to isolate end-user code from the actual table names. Ideally, the actual table names should only exist in one place in the entire code base: in the class definitions for each L<Rose::DB::OBject>-derived class.
4241              
4242             That said, when using L<Rose::DB::Object::Manager>, the actual table names can be used as well. But be aware that some databases don't like a mix of table aliases and real table names in some kinds of queries.
4243              
4244             Valid parameters to L<get_objects|/get_objects> are:
4245              
4246             =over 4
4247              
4248             =item B<allow_empty_lists BOOL>
4249              
4250             If set to true, C<query> parameters with empty lists as values are allowed. For example:
4251              
4252             @ids = (); # empty list
4253              
4254             Product::Manager->get_products(
4255             query =>
4256             [
4257             id => \@ids,
4258             ...
4259             ]);
4260              
4261             By default, passing an empty list as a value will cause a fatal error.
4262              
4263             =item B<db DB>
4264              
4265             A L<Rose::DB>-derived object used to access the database. If omitted, one will be created by calling the L<init_db|Rose::DB::Object/init_db> method of the C<object_class>.
4266              
4267             =item B<debug BOOL>
4268              
4269             If true, print the generated SQL to STDERR.
4270              
4271             =item B<distinct [ BOOL | ARRAYREF ]>
4272              
4273             If set to any kind of true value, then the "DISTINCT" SQL keyword will be added to the "SELECT" statement. Specific values trigger the behaviors described below.
4274              
4275             If set to a simple scalar value that is true, then only the columns in the primary table ("t1") are fetched from the database.
4276              
4277             If set to a reference to an array of table names, "tN" table aliases, or relationship or foreign key names, then only the columns from the corresponding tables will be fetched. In the case of relationships that involve more than one table, only the "most distant" table is considered. (e.g., The map table is ignored in a "many to many" relationship.) Columns from the primary table ("t1") are always selected, regardless of whether or not it appears in the list.
4278              
4279             This parameter conflicts with the C<fetch_only> parameter in the case where both provide a list of table names or aliases. In this case, if the value of the C<distinct> parameter is also reference to an array table names or aliases, then a fatal error will occur.
4280              
4281             =item B<fetch_only ARRAYREF>
4282              
4283             ARRAYREF should be a reference to an array of table names or "tN" table aliases. Only the columns from the corresponding tables will be fetched. In the case of relationships that involve more than one table, only the "most distant" table is considered. (e.g., The map table is ignored in a "many to many" relationship.) Columns from the primary table ("t1") are always selected, regardless of whether or not it appears in the list.
4284              
4285             This parameter conflicts with the C<distinct> parameter in the case where both provide a list of table names or aliases. In this case, then a fatal error will occur.
4286              
4287             =item B<for_update BOOL>
4288              
4289             If true, this parameter is translated to be the equivalent of passing the L<lock|/lock> parameter and setting the C<type> to C<for update>. For example, this:
4290              
4291             for_update => 1
4292              
4293             is equivalent to this:
4294              
4295             lock => { type => 'for update' }
4296              
4297             See the L<lock|/lock> parameter below for more information.
4298              
4299             =item B<hints HASHREF>
4300              
4301             A reference to a hash of hints that influence the SQL generated to fetch the objects. Hints are just "suggestions" and may be ignored, depending on the actual features of the database being queried. Use the L<debug|/debug> parameter to see the generated SQL. Most of the current hints apply to MySQL only. See the relevant documentation for more details:
4302              
4303             L<http://dev.mysql.com/doc/refman/5.0/en/select.html>
4304              
4305             The hints hash is keyed by tN table aliases or relationship names. The value of each key is a reference to a hash of hint directives. In the absence of any key for "t1" or the name of the primary table, the entire hints hash is considered applicable to the primary table.
4306              
4307             Valid hint directives are:
4308              
4309             =over 4
4310              
4311             =item B<all_rows BOOL>
4312              
4313             If true, direct the database to choose the query plan that returns all the records as quickly as possible.
4314              
4315             =item B<big_result BOOL>
4316              
4317             If true, indicate to the database that the result set is expected to be big.
4318              
4319             =item B<buffer_result BOOL>
4320              
4321             If true, force the result to be put into a temporary table.
4322              
4323             =item B<cache BOOL>
4324              
4325             If true, ask the database to store the result in its query cache.
4326              
4327             =item B<calc_found_rows BOOL>
4328              
4329             If true, ask the database to internally calculate the number of rows found, ignoring any L<limit|/limit> or L<offset|/offset> arguments.
4330              
4331             =item B<comment TEXT>
4332              
4333             Add a comment after the "SELECT" keyword in the query. TEXT should B<not> be surrounded by any comment delimiters. The appropriate delimiters will be added automatically.
4334              
4335             =item B<first_rows BOOL>
4336              
4337             If true, direct the database to choose the query plan that returns the first result record as soon as possible.
4338              
4339             =item B<force_index [ INDEX | ARRAYREF ]>
4340              
4341             Force the use of the named indexes, specified by an index name or a reference to an array of index names.
4342              
4343             =item B<high_priority BOOL>
4344              
4345             If true, give this query higher priority.
4346              
4347             =item B<ignore_index [ INDEX | ARRAYREF ]>
4348              
4349             Ignore the named indexes, specified by an index name or a reference to an array of index names.
4350              
4351             =item B<no_cache BOOL>
4352              
4353             If true, ask the database not to store the result in its query cache.
4354              
4355             =item B<small_result BOOL>
4356              
4357             If true, indicate to the database that the result set is expected to be small.
4358              
4359             =item B<straight_join BOOL>
4360              
4361             If true, ask the database to join the tables in the order that they are listed in the "FROM" clause of the SQL statement.
4362              
4363             =item B<strict_ops BOOL>
4364              
4365             If true, any comparison operator used in the C<query> that is not listed in the L<Rose::DB::Object::QueryBuilder> documentation will cause a fatal error. The default value is determined by the L<strict_ops|/strict_ops> class method.
4366              
4367             =item B<use_index [ INDEX | ARRAYREF ]>
4368              
4369             Prefer to use the named indexes, specified by an index name or a reference to an array of index names.
4370              
4371             =back
4372              
4373             =item B<inject_results BOOL>
4374              
4375             If true, then the data returned from the database will be directly "injected" into the objects returned by this method, bypassing the constructor and column mutator methods for each object class. The default is false. This parameter is ignored (i.e., treated as if it were false) if the C<select> parameter is passed.
4376              
4377             This parameter is useful for situations where the performance of L<get_objects|/get_objects> is limited by the speed at which L<Rose::DB::Object>-derived objects can be created. It's safe to set this parameter to true only if the constructor and column mutator methods for all of the classes involved do not have any side-effects (or if it's is okay to bypass any side-effects).
4378              
4379             The default L<Rose::DB::Object> L<constructor|Rose::DB::Object/new> and the column mutator methods created by the column classes included in the L<Rose::DB::Object> module distribution do not have any side-effects and should therefore be safe to use with this parameter.
4380              
4381             =item B<limit NUM>
4382              
4383             Return a maximum of NUM objects.
4384              
4385             =item B<limit_with_subselect BOOL>
4386              
4387             This parameter controls whether or not this method will consider using a sub-query to express C<limit>/C<offset> constraints when fetching sub-objects related through one of the "...-to-many" relationship types. Not all databases support this syntax, and not all queries can use it even in supported databases. If this parameter is true, the feature will be used when possible.
4388              
4389             The default value is determined by the L<default_limit_with_subselect|/default_limit_with_subselect> class method.
4390              
4391             =item B<lock [ TYPE | HASHREF ]>
4392              
4393             Select the objects using some form of locking. These lock directives have database-specific behavior and not all directives are supported by all databases. Consult your database's documentation to find out more. Use the L<debug|/debug> parameter to see the generated SQL.
4394              
4395             The value should be a reference to a hash or a TYPE string, which is equivalent to setting the value of the C<type> key in the hash reference form. For example, these are both equivalent:
4396              
4397             lock => 'for update'
4398             lock => { type => 'for update' }
4399              
4400             Valid hash keys are:
4401              
4402             =over 4
4403              
4404             =item B<columns ARRAYREF>
4405              
4406             A reference to an array of column names to lock. The columns may be prefixed with their table name or their C<tN> alias (e.g., C<mytable.mycol> or C<t2.mycol>) or left unadorned if they are not ambiguous. References to scalars will be de-referenced and used as-is, included literally in the SQL locking clause.
4407              
4408             =item B<nowait BOOL>
4409              
4410             If true, do not wait to acquire the lock. If supported, this is usually by adding a C<NOWAIT> directive to the SQL.
4411              
4412             =item B<on ARRAYREF>
4413              
4414             A reference to an array of items to lock. Depending on the database, these may be column or tables. Both column and table names should be specified using dot-separated relationship paths.
4415              
4416             For example, C<vendor.region.name> would lock the C<name> column in the table arrived at by traversing the C<vendor> and then the C<region> relationships, starting from the primary table (C<t1>). Lone column names may also be used, provided they're not ambiguous.
4417              
4418             For locking whole tables, C<vendor.region> would lock the table arrived at by traversing the C<vendor> and then the C<region> relationships. (See the L<require_objects|/require_objects> parameter for more information on relationship traversal.)
4419              
4420             Finally, references to scalars will be de-referenced and used as-is, included literally in the SQL locking clause.
4421              
4422             =item B<skip_locked BOOL>
4423              
4424             If true, skip any locked rows. If supported, this is usually by adding a C<SKIP LOCKED> clause to the SQL.
4425              
4426             =item B<tables ARRAYREF>
4427              
4428             A reference to an array of tables to lock. Table named or C<tN> aliases may be used. References to scalars will be de-referenced and used as-is, included literally in the SQL locking clause.
4429              
4430             =item B<type TYPE>
4431              
4432             The type of lock to acquire. Valid values for TYPE are C<for update> and C<shared>. This hash key is required unless the L<for_update|/for_update> parameter was passed with a true value.
4433              
4434             =item B<wait TIME>
4435              
4436             Wait for the specified TIME (generally seconds) before giving up acquiring the lock. If supported, this is usually by adding a C<WAIT ...> clause to the SQL.
4437              
4438             =back
4439              
4440             You may pass only one of the parameters that specifies "what to lock" (i.e., C<columns>, C<on>, or C<tables>).
4441              
4442             =item B<nested_joins BOOL>
4443              
4444             This parameter controls whether or not this method will consider using nested JOIN syntax when fetching related objects. Not all databases support this syntax, and not all queries will use it even in supported databases. If this parameter is true, the feature will be used when possible.
4445              
4446             The default value is determined by the L<default_nested_joins|/default_nested_joins> class method.
4447              
4448             =item B<multi_many_ok BOOL>
4449              
4450             If true, do not print a warning when attempting to do multiple LEFT OUTER JOINs against tables related by "... to many" relationships. See the documentation for the C<with_objects> parameter for more information.
4451              
4452             =item B<nonlazy [ BOOL | ARRAYREF ]>
4453              
4454             By default, L<get_objects|/get_objects> will honor all L<load-on-demand columns|Rose::DB::Object::Metadata::Column/load_on_demand> when fetching objects. Use this parameter to override that behavior and select all columns instead.
4455              
4456             If the value is a true boolean value (typically "1"), then all columns will be fetched for all participating classes (i.e., the main object class as well as any sub-object classes).
4457              
4458             The value can also be a reference to an array of relationship names. The sub-objects corresponding to each relationship name will have all their columns selected. To refer to the main class (the "t1" table), use the special name "self".
4459              
4460             =item B<object_args HASHREF>
4461              
4462             A reference to a hash of name/value pairs to be passed to the constructor of each C<object_class> object fetched, in addition to the values from the database.
4463              
4464             =item B<object_class CLASS>
4465              
4466             The name of the L<Rose::DB::Object>-derived objects to be fetched. This parameter is required; a fatal error will occur if it is omitted. Defaults to the value returned by the L<object_class|/object_class> class method.
4467              
4468             =item B<offset NUM>
4469              
4470             Skip the first NUM rows. If the database supports some sort of "limit with offset" syntax (e.g., "LIMIT 10 OFFSET 20") then it will be used. Otherwise, the first NUM rows will be fetched and then discarded.
4471              
4472             This parameter can only be used along with the C<limit> parameter, otherwise a fatal error will occur.
4473              
4474             =item B<page NUM>
4475              
4476             Show page number NUM of objects. Pages are numbered starting from 1. A page number less than or equal to zero causes the page number to default to 1.
4477              
4478             The number of objects per page can be set by the C<per_page> parameter. If the C<per_page> parameter is supplied and this parameter is omitted, it defaults to 1 (the first page).
4479              
4480             If this parameter is included along with either of the C<limit> or <offset> parameters, a fatal error will occur.
4481              
4482             =item B<per_page NUM>
4483              
4484             The number of objects per C<page>. Defaults to the value returned by the L<default_objects_per_page|/default_objects_per_page> class method (20, by default).
4485              
4486             If this parameter is included along with either of the C<limit> or <offset> parameters, a fatal error will occur.
4487              
4488             =item B<prepare_cached BOOL>
4489              
4490             If true, then L<DBI>'s L<prepare_cached|DBI/prepare_cached> method will be used (instead of the L<prepare|DBI/prepare> method) when preparing the SQL statement that will fetch the objects. If omitted, the default value is determined by the L<dbi_prepare_cached|/dbi_prepare_cached> class method.
4491              
4492             =item B<query ARRAYREF>
4493              
4494             The query parameters, passed as a reference to an array of name/value pairs. These pairs are used to formulate the "where" clause of the SQL query that, in turn, is used to fetch the objects from the database. Arbitrarily nested boolean logic is supported.
4495              
4496             For the complete list of valid parameter names and values, see the documentation for the C<query> parameter of the L<build_select|Rose::DB::Object::QueryBuilder/build_select> function in the L<Rose::DB::Object::QueryBuilder> module.
4497              
4498             This class also supports an extension to the query syntax supported by L<Rose::DB::Object::QueryBuilder>. In addition to table names and aliases, column (or column method) names may be prefixed with foreign key or relationship names. These names may be chained, with dots (".") separating the components.
4499              
4500             For example, imagine three tables, C<products>, C<vendors>, and C<regions>, fronted by three L<Rose::DB::Object>-derived classes, C<Product>, C<Vendor>, and C<Region>, respectively. Each C<Product> has a C<Vendor>, and each C<Vendor> has a C<Region>.
4501              
4502             To select only products whose vendors are in the United States, use a query argument like this:
4503              
4504             query => [ 'vendor.region.name' => 'US' ],
4505              
4506             This assumes that the C<Product> class has a relationship or foreign key named "vendor" that points to the product's C<Vendor>, and that the C<Vendor> class has a foreign key or relationship named "region" that points to the vendor's C<Region>, and that 'vendor.region' (or any foreign key or relationship name chain that begins with 'vendor.region.') is an argument to the C<with_objects> or C<require_objects> parameters.
4507              
4508             Please note that the "L<tN|Rose::DB::Object::QueryBuilder/tables>" table aliases are not allowed in front of these kinds of chained relationship parameters. (The chain of relationship names specifies the target table, so any "tN" alias would be redundant at best, or present a conflict at worst.)
4509              
4510             =item B<require_objects ARRAYREF>
4511              
4512             Only fetch rows from the primary table that have all of the associated sub-objects listed in ARRAYREF, a reference to an array of L<foreign key|Rose::DB::Object::Metadata/foreign_keys> or L<relationship|Rose::DB::Object::Metadata/relationships> names defined for C<object_class>. The supported relationship types are "L<one to one|Rose::DB::Object::Metadata::Relationship::OneToOne>," "L<one to many|Rose::DB::Object::Metadata::Relationship::OneToMany>," and "L<many to many|Rose::DB::Object::Metadata::Relationship::ManyToMany>".
4513              
4514             For each foreign key or relationship name listed in ARRAYREF, another table will be added to the query via an implicit inner join. The join conditions will be constructed automatically based on the foreign key or relationship definitions. Note that each related table must have a L<Rose::DB::Object>-derived class fronting it.
4515              
4516             Foreign key and relationship names may be chained, with dots (".") separating each name. For example, imagine three tables, C<products>, C<vendors>, and C<regions>, fronted by three L<Rose::DB::Object>-derived classes, C<Product>, C<Vendor>, and C<Region>, respectively. Each C<Product> has a C<Vendor>, and each C<Vendor> has a C<Region>.
4517              
4518             To fetch C<Product>s along with their C<Vendor>s, and their vendors' C<Region>s, provide a C<with_objects> argument like this:
4519              
4520             require_objects => [ 'vendor.region' ],
4521              
4522             This assumes that the C<Product> class has a relationship or foreign key named "vendor" that points to the product's C<Vendor>, and that the C<Vendor> class has a foreign key or relationship named "region" that points to the vendor's C<Region>.
4523              
4524             This chaining syntax can be used to traverse relationships of any kind, including "one to many" and "many to many" relationships, to an arbitrary depth.
4525              
4526             The following optional suffixes may be added after any name in the chain in order to override the join type used:
4527              
4528             Suffix Join Type
4529             ------ ----------
4530             ! Inner join
4531             ? Left outer join
4532              
4533             Each link in a C<require_objects> chain uses an inner join by default. In other words, the following C<require_objects> parameters are all equivalent:
4534              
4535             # These all mean the same thing
4536             require_objects => [ 'vendor.region' ]
4537             require_objects => [ 'vendor!.region!' ]
4538             require_objects => [ 'vendor.region!' ]
4539             require_objects => [ 'vendor!.region' ]
4540              
4541             Thus, it is only really useful to use the C<?> suffix in C<require_objects> parameters (though the C<!> suffixes don't do any harm). Here's a useful example of a call with hybrid join chain:
4542              
4543             $products =
4544             Product::Manager->get_products(
4545             require_objects => [ 'vendor.region?' ]);
4546              
4547             All product objects returned would have associated vendor objects, but those vendor objects may or may not have associated region objects.
4548              
4549             Note that inner joins may be implicit and L<nested_joins|/nested_joins> may or may not be used. When in doubt, use the L<debug|/debug> parameter to see the generated SQL.
4550              
4551             B<Warning:> there may be a geometric explosion of redundant data returned by the database if you include more than one "... to many" relationship in ARRAYREF. Sometimes this may still be more efficient than making additional queries to fetch these sub-objects, but that all depends on the actual data. A warning will be emitted (via L<Carp::cluck|Carp/cluck>) if you include more than one "... to many" relationship in ARRAYREF. If you're sure you know what you're doing, you can silence this warning by passing the C<multi_many_ok> parameter with a true value.
4552              
4553             B<Note:> the C<require_objects> list currently cannot be used to simultaneously fetch two objects that both front the same database table, I<but are of different classes>. One workaround is to make one class use a synonym or alias for one of the tables. Another option is to make one table a trivial view of the other. The objective is to get the table names to be different for each different class (even if it's just a matter of letter case, if your database is not case-sensitive when it comes to table names).
4554              
4555             =item B<select [ CLAUSE | ARRAYREF ]>
4556              
4557             Select only the columns specified in either a comma-separated string of column names or a reference to an array of column names. Strings are naively split between each comma. If you need more complex parsing, please use the array-reference argument format instead.
4558              
4559             Column names should be prefixed by the appropriate "tN" table alias, the table name, or the foreign key or relationship name. The prefix should be joined to the column name with a dot ("."). Examples: C<t2.name>, C<vendors.age>.
4560              
4561             Unprefixed columns are assumed to belong to the primary table ("t1") and are explicitly prefixed as such when selecting from more than one table. If a column name matches C</ AS \w+$/> then no prefix is applied.
4562              
4563             If the column name is "*" (e.g., C<t1.*>) then all columns from that table are selected.
4564              
4565             If an item in the referenced array is itself a reference to a scalar, then that item will be dereferenced and passed through unmodified.
4566              
4567             If selecting sub-objects via the C<with_objects> or C<require_objects> parameters, you must select the primary key columns from each sub-object table. Failure to do so will cause those sub-objects I<not> to be created.
4568              
4569             Be warned that you should provide some way to determine which column or method and which class an item belongs to: a tN prefix, a column name, or at the very least an "... AS ..." alias clause.
4570              
4571             If any C<with_objects> or C<require_objects> arguments are included in this call, the C<select> list must include at least the primary key column(s) from each table that contributes to the named relationships.
4572              
4573             This parameter conflicts with the C<fetch_only> parameter. A fatal error will occur if both are used in the same call.
4574              
4575             If this parameter is omitted, then all columns from all participating tables are selected (optionally modified by the C<nonlazy> parameter).
4576              
4577             =item B<share_db BOOL>
4578              
4579             If true, C<db> will be passed to each L<Rose::DB::Object>-derived object when it is constructed. Defaults to true.
4580              
4581             =item B<sort_by [ CLAUSE | ARRAYREF ]>
4582              
4583             A fully formed SQL "ORDER BY ..." clause, sans the words "ORDER BY", or a reference to an array of strings or scalar references to be de-referenced as needed, joined with a comma, and appended to the "ORDER BY" clause.
4584              
4585             If an argument is a reference to a scalar, then it is passed through to the ORDER BY clause unmodified.
4586              
4587             Otherwise, within each string, any instance of "NAME." will be replaced with the appropriate "tN." table alias, where NAME is a table, foreign key, or relationship name. All unprefixed simple column names are assumed to belong to the primary table ("t1").
4588              
4589             If selecting sub-objects (via C<require_objects> or C<with_objects>) that are related through "one to many" or "many to many" relationships, the first condition in the sort order clause must be a column in the primary table (t1). If this condition is not met, the list of primary key columns will be added to the beginning of the sort order clause automatically.
4590              
4591             =item B<table_aliases BOOL>
4592              
4593             When only a single table is used in q auery, this parameter controls whether or not the "tN" aliases are used. If the parameter is not passed, then tables are aliased. If it is passed with a false value, then tables are not aliased. When more than one table participates in a query, the "tN" table aliases are always used and this option is ignored.
4594              
4595             =item B<unique_aliases BOOL>
4596              
4597             If true, and if there is no explicit value for the C<select> parameter and more than one table is participating in the query, then each selected column will be given a unique alias by prefixing it with its table alias and an underscore. The default value is false. Example:
4598              
4599             SELECT
4600             t1.id AS t1_id,
4601             t1.name AS t1_name,
4602             t2.id AS t2_id,
4603             t2.name AS t2_name
4604             FROM
4605             foo AS t1,
4606             bar AS t2
4607             WHERE
4608             ...
4609              
4610             These unique aliases provide a technique of last resort for unambiguously addressing a column in a query clause.
4611              
4612             =item B<where ARRAYREF>
4613              
4614             This is an alias for the C<query> parameter (see above).
4615              
4616             =item B<with_map_records [ BOOL | METHOD | HASHREF ]>
4617              
4618             When fetching related objects through a "L<many to many|Rose::DB::Object::Metadata::Relationship::ManyToMany>" relationship, objects of the L<map class|Rose::DB::Object::Metadata::Relationship::ManyToMany/map_class> are not retrieved by default. Use this parameter to override the default behavior.
4619              
4620             If the value is "1", then each object fetched through a mapping table will have its associated map record available through a C<map_record()> attribute.
4621              
4622             If a method name is provided instead, then each object fetched through a mapping table will have its associated map record available through a method of that name.
4623              
4624             If the value is a reference to a hash, then the keys of the hash should be "many to many" relationship names, and the values should be the method names through which the maps records will be available for each relationship.
4625              
4626             =item B<with_objects ARRAYREF>
4627              
4628             Also fetch sub-objects (if any) associated with rows in the primary table based on a reference to an array of L<foreign key|Rose::DB::Object::Metadata/foreign_keys> or L<relationship|Rose::DB::Object::Metadata/relationships> names defined for C<object_class>. The supported relationship types are "L<one to one|Rose::DB::Object::Metadata::Relationship::OneToOne>," "L<one to many|Rose::DB::Object::Metadata::Relationship::OneToMany>," and "L<many to many|Rose::DB::Object::Metadata::Relationship::ManyToMany>".
4629              
4630             For each foreign key or relationship name listed in ARRAYREF, another table will be added to the query via an explicit LEFT OUTER JOIN. (Foreign keys whose columns are all NOT NULL are the exception, however. They are always fetched via inner joins.) The join conditions will be constructed automatically based on the foreign key or relationship definitions. Note that each related table must have a L<Rose::DB::Object>-derived class fronting it. See the L<synopsis|/SYNOPSIS> for an example.
4631              
4632             "Many to many" relationships are a special case. They will add two tables to the query (the "map" table plus the table with the actual data), which will offset the "tN" table numbering by one extra table.
4633              
4634             Foreign key and relationship names may be chained, with dots (".") separating each name. For example, imagine three tables, C<products>, C<vendors>, and C<regions>, fronted by three L<Rose::DB::Object>-derived classes, C<Product>, C<Vendor>, and C<Region>, respectively. Each C<Product> has a C<Vendor>, and each C<Vendor> has a C<Region>.
4635              
4636             To fetch C<Product>s along with their C<Vendor>s, and their vendors' C<Region>s, provide a C<with_objects> argument like this:
4637              
4638             with_objects => [ 'vendor.region' ],
4639              
4640             This assumes that the C<Product> class has a relationship or foreign key named "vendor" that points to the product's C<Vendor>, and that the C<Vendor> class has a foreign key or relationship named "region" that points to the vendor's C<Region>.
4641              
4642             This chaining syntax can be used to traverse relationships of any kind, including "one to many" and "many to many" relationships, to an arbitrary depth.
4643              
4644             The following optional suffixes may be added after any name in the chain in order to override the join type used:
4645              
4646             Suffix Join Type
4647             ------ ----------
4648             ! Inner join
4649             ? Left outer join
4650              
4651             Each link in a C<with_objects> chain uses a left outer join by default. In other words, the following C<with_objects> parameters are all equivalent:
4652              
4653             # These all mean the same thing
4654             with_objects => [ 'vendor.region' ]
4655             with_objects => [ 'vendor?.region?' ]
4656             with_objects => [ 'vendor.region?' ]
4657             with_objects => [ 'vendor?.region' ]
4658              
4659             Thus, it is only really useful to use the C<!> suffix in C<with_objects> parameters (though the C<?> suffixes don't do any harm). Here's a useful example of a call with hybrid join chain:
4660              
4661             $products =
4662             Product::Manager->get_products(
4663             with_objects => [ 'vendor!.region' ]);
4664              
4665             All product objects returned would have associated vendor objects, but those vendor object may or may not have associated region objects.
4666              
4667             Note that inner joins may be implicit and L<nested_joins|/nested_joins> may or may not be used. When in doubt, use the L<debug|/debug> parameter to see the generated SQL.
4668              
4669             B<Warning:> there may be a geometric explosion of redundant data returned by the database if you include more than one "... to many" relationship in ARRAYREF. Sometimes this may still be more efficient than making additional queries to fetch these sub-objects, but that all depends on the actual data. A warning will be emitted (via L<Carp::cluck|Carp/cluck>) if you include more than one "... to many" relationship in ARRAYREF. If you're sure you know what you're doing, you can silence this warning by passing the C<multi_many_ok> parameter with a true value.
4670              
4671             B<Note:> the C<with_objects> list currently cannot be used to simultaneously fetch two objects that both front the same database table, I<but are of different classes>. One workaround is to make one class use a synonym or alias for one of the tables. Another option is to make one table a trivial view of the other. The objective is to get the table names to be different for each different class (even if it's just a matter of letter case, if your database is not case-sensitive when it comes to table names).
4672              
4673             =back
4674              
4675             =item B<get_objects_count [PARAMS]>
4676              
4677             Accepts the same arguments as L<get_objects|/get_objects>, but just returns the number of objects that would have been fetched, or undef if there was an error.
4678              
4679             =item B<get_objects_from_sql [ SQL | PARAMS ]>
4680              
4681             Fetch objects using a custom SQL query. Pass either a single SQL query string or name/value parameters as arguments. Valid parameters are:
4682              
4683             =over 4
4684              
4685             =item B<args ARRAYREF>
4686              
4687             A reference to an array of arguments to be passed to L<DBI>'s L<execute|DBI/execute> method when the query is run. The number of items in this array must exactly match the number of placeholders in the SQL query.
4688              
4689             =item B<db DB>
4690              
4691             A L<Rose::DB>-derived object used to access the database. If omitted, one will be created by calling the L<init_db|Rose::DB::Object/init_db> method of the C<object_class>.
4692              
4693             =item B<object_class CLASS>
4694              
4695             The class name of the L<Rose::DB::Object>-derived objects to be fetched. Defaults to the value returned by the L<object_class|/object_class> class method.
4696              
4697             =item B<prepare_cached BOOL>
4698              
4699             If true, then L<DBI>'s L<prepare_cached|DBI/prepare_cached> method will be used (instead of the L<prepare|DBI/prepare> method) when preparing the SQL statement that will fetch the objects. If omitted, the default value is determined by the L<dbi_prepare_cached|/dbi_prepare_cached> class method.
4700              
4701             =item B<prepare_options HASHREF>
4702              
4703             A reference to a hash of attributes to be passed to L<DBI>'s L<prepare|DBI/prepare> or L<prepare_cached|DBI/prepare_cached> method when preparing the SQL statement.
4704              
4705             =item B<share_db BOOL>
4706              
4707             If true, C<db> will be passed to each L<Rose::DB::Object>-derived object when it is constructed. Defaults to true.
4708              
4709             =item B<sql SQL>
4710              
4711             The SQL query string. This parameter is required.
4712              
4713             =back
4714              
4715             Each column returned by the SQL query must be either a column or method name in C<object_class>. Column names take precedence in the case of a conflict.
4716              
4717             Returns a reference to an array of C<object_class> objects.
4718              
4719             Examples:
4720              
4721             package Product::Manager;
4722             use Product;
4723             use base 'Rose::DB::Object::Manager';
4724             sub object_class { 'Product' }
4725             ...
4726              
4727             $products = Product::Manager->get_objects_from_sql(<<"EOF");
4728             SELECT * FROM products WHERE sku % 2 != 0 ORDER BY status, type
4729             EOF
4730              
4731             $products =
4732             Product::Manager->get_objects_from_sql(
4733             args => [ '2005-01-01' ],
4734             sql => 'SELECT * FROM products WHERE release_date > ?');
4735              
4736             =item B<get_objects_iterator [PARAMS]>
4737              
4738             Accepts any valid L<get_objects|/get_objects> arguments, but return a L<Rose::DB::Object::Iterator> object, or undef if there was an error.
4739              
4740             =item B<get_objects_iterator_from_sql [PARAMS]>
4741              
4742             Accepts any valid L<get_objects_from_sql|/get_objects_from_sql> arguments, but return a L<Rose::DB::Object::Iterator> object, or undef if there was an error.
4743              
4744             =item B<get_objects_sql [PARAMS]>
4745              
4746             Accepts the same arguments as L<get_objects|/get_objects>, but return the SQL query string that would have been used to fetch the objects (in scalar context), or the SQL query string and a reference to an array of bind values (in list context).
4747              
4748             =item B<make_manager_methods PARAMS>
4749              
4750             Create convenience wrappers for L<Rose::DB::Object::Manager>'s L<get_objects|/get_objects>, L<get_objects_iterator|/get_objects_iterator>, and L<get_objects_count|/get_objects_count> class methods in the target class. These wrapper methods will not overwrite any existing methods in the target class. If there is an existing method with the same name, a fatal error will occur.
4751              
4752             PARAMS can take several forms, depending on the calling context. For a call to L<make_manager_methods|/make_manager_methods> to succeed, the following information must be determined:
4753              
4754             =over 4
4755              
4756             =item * B<object class>
4757              
4758             The class of the L<Rose::DB::Object>-derived objects to be fetched or counted.
4759              
4760             =item * B<base name> or B<method name>
4761              
4762             The base name is a string used as the basis of the method names. For example, the base name "products" might be used to create methods named "get_B<products>", "get_B<products>_count", "get_B<products>_iterator", "delete_B<products>", and "update_B<products>".
4763              
4764             In the absence of a base name, an explicit method name may be provided instead. The method name will be used as is.
4765              
4766             =item * B<method types>
4767              
4768             The types of methods that should be generated. Each method type is a wrapper for a L<Rose::DB::Object::Manager> class method. The mapping of method type names to actual L<Rose::DB::Object::Manager> class methods defaults to the following:
4769              
4770             Type Method
4771             -------- ----------------------
4772             objects get_objects()
4773             iterator get_objects_iterator()
4774             count get_objects_count()
4775             delete delete_objects()
4776             update update_objects()
4777              
4778             You may override the L<auto_manager_method_name|Rose::DB::Object::ConventionManager/auto_manager_method_name> method in the L<object_class|/object_class>'s L<convention manager|Rose::DB::Object::Metadata/convention_manager> class to customize one or more of these names.
4779              
4780             =item * B<target class>
4781              
4782             The class that the methods should be installed in.
4783              
4784             =back
4785              
4786             Here are all of the different ways that each of those pieces of information can be provided, either implicitly or explicitly as part of PARAMS.
4787              
4788             =over 4
4789              
4790             =item * B<object class>
4791              
4792             If an C<object_class> parameter is passed in PARAMS, then its value is used as the object class. Example:
4793              
4794             $class->make_manager_methods(object_class => 'Product', ...);
4795              
4796             If the C<object_class> parameter is not passed, and if the B<target class> inherits from L<Rose::DB::Object::Manager> and has also defined an C<object_class> method, then the return value of that method is used as the object class. Example:
4797              
4798             package Product::Manager;
4799              
4800             use Rose::DB::Object::Manager;
4801             our @ISA = qw(Rose::DB::Object::Manager);
4802              
4803             sub object_class { 'Product' }
4804              
4805             # Assume object_class parameter is not part of the ... below
4806             __PACKAGE__->make_manager_methods(...);
4807              
4808             In this case, the object class would be C<Product>.
4809              
4810             Finally, if none of the above conditions are met, one final option is considered. If the B<target class> inherits from L<Rose::DB::Object>, then the object class is set to the B<target class>.
4811              
4812             If the object class cannot be determined in one of the ways described above, then a fatal error will occur.
4813              
4814             =item * B<base name> or B<method name>
4815              
4816             If a C<base_name> parameter is passed in PARAMS, then its value is used as the base name for the generated methods. Example:
4817              
4818             $class->make_manager_methods(base_name => 'products', ...);
4819              
4820             If the C<base_name> parameter is not passed, and if there is only one argument passed to the method, then the lone argument is used as the base name. Example:
4821              
4822             $class->make_manager_methods('products');
4823              
4824             (Note that, since the B<object class> must be derived somehow, this will only work in one of the situations (described above) where the B<object class> can be derived from the calling context or class.)
4825              
4826             If a C<methods> parameter is passed with a hash ref value, then each key of the hash is used as the base name for the method types listed in the corresponding value. (See B<method types> below for more information.)
4827              
4828             If a key of the C<methods> hash ends in "()", then it is taken as the method name and is used as is. For example, the key "foo" will be used as a base name, but the key "foo()" will be used as a method name.
4829              
4830             If the base name cannot be determined in one of the ways described above, then the L<auto_manager_base_name|Rose::DB::Object::ConventionManager/auto_manager_base_name> method in the L<object_class|/object_class>'s L<convention manager|Rose::DB::Object::Metadata/convention_manager> is called on to supply a base name.
4831              
4832             =item * B<method types>
4833              
4834             If an explicit list of method types is not passed to the method, then all of the L<default_manager_method_types|/default_manager_method_types> are created. Example:
4835              
4836             # Base name is determined by convention manager auto_manager_base_name()
4837             # method, all default method types created
4838             $class->make_manager_methods();
4839              
4840             # Base name is "products", all default method types created
4841             $class->make_manager_methods('products');
4842              
4843             # Base name is "products", all default method types created
4844             $class->make_manager_methods(base_name => products', ...);
4845              
4846             (Again, note that the B<object class> must be derived somehow.)
4847              
4848             If a C<methods> parameter is passed, then its value must be a reference to a hash whose keys are base names or method names, and whose values are method types or references to arrays of method types.
4849              
4850             If a key ends in "()", then it is taken as a method name and is used as is. Otherwise, it is used as a base name. For example, the key "foo" will be used as a base name, but the key "foo()" will be used as a method name.
4851              
4852             If a key is a method name and its value specifies more than one method type, then a fatal error will occur. (It's impossible to have more than one method with the same name.)
4853              
4854             Example:
4855              
4856             # Make the following methods:
4857             #
4858             # * Base name: products; method types: objects, iterators
4859             #
4860             # get_products()
4861             # get_products_iterator()
4862             #
4863             # * Method name: product_count; method type: count
4864             #
4865             # product_count()
4866             #
4867             $class->make_manager_methods(...,
4868             methods =>
4869             {
4870             'products' => [ qw(objects iterator) ],
4871             'product_count()' => 'count'
4872             });
4873              
4874             If the value of the C<methods> parameter is not a reference to a hash, or if both the C<methods> and C<base_name> parameters are passed, then a fatal error will occur.
4875              
4876             =item * B<target class>
4877              
4878             If a C<target_class> parameter is passed in PARAMS, then its value is used as the target class. Example:
4879              
4880             $class->make_manager_methods(target_class => 'Product', ...);
4881              
4882             If a C<target_class> parameter is not passed, and if the calling class is not L<Rose::DB::Object::Manager>, then the calling class is used as the target class. Otherwise, the class from which the method was called is used as the target class. Examples:
4883              
4884             # Target class is Product, regardless of the calling
4885             # context or the value of $class
4886             $class->make_manager_methods(target_class => 'Product', ...);
4887              
4888             package Foo;
4889              
4890             # Target class is Foo: no target_class parameter is passed
4891             # and the calling class is Rose::DB::Object::Manager, so
4892             # the class from which the method was called (Foo) is used.
4893             Rose::DB::Object::Manager->make_manager_methods(
4894             object_class => 'Bar',
4895             base_name => 'Baz');
4896              
4897             package Bar;
4898              
4899             # Target class is Foo: no target_class parameter is passed
4900             # and the calling class is not Rose::DB::Object::Manager,
4901             # so the calling class (Foo) is used.
4902             Foo->make_manager_methods(object_class => 'Bar',
4903             base_name => 'Baz');
4904              
4905             =back
4906              
4907             There's a lot of flexibility in this method's arguments (although some might use the word "confusion" instead), but the examples can be pared down to a few common usage scenarios.
4908              
4909             The first is the recommended technique, as seen in the L<synopsis|/SYNOPSIS>. Create a separate manager class that inherits from L<Rose::DB::Object::Manager>, override the C<object_class> method to specify the class of the objects being fetched, and then pass a lone base name argument to the call to L<make_manager_methods|/make_manager_methods>.
4910              
4911             package Product::Manager;
4912              
4913             use Rose::DB::Object::Manager;
4914             our @ISA = qw(Rose::DB::Object::Manager);
4915              
4916             sub object_class { 'Product' }
4917              
4918             __PACKAGE__->make_manager_methods('products');
4919              
4920             The second example is used to install object manager methods directly into a L<Rose::DB::Object>-derived class. I do not recommend this practice; I consider it "semantically impure" for the class that represents a single object to also be the class that's used to fetch multiple objects. Inevitably, classes grow, and I'd like the "object manager" class to be separate from the object class itself so they can grow happily in isolation, with no potential clashes.
4921              
4922             Also, keep in mind that L<Rose::DB::Object> and L<Rose::DB::Object::Manager> have separate L<error_mode|/error_mode> settings which must be synchronized or otherwise dealt with. Another advantage of using a separate L<Rose::DB::Object::Manager> subclass (as described earlier) is that you can override the L<error_mode|Rose::DB::Object::Manager/error_mode> in your L<Rose::DB::Object::Manager> subclass only, rather than overriding the base class L<Rose::DB::Object::Manager error_mode|Rose::DB::Object::Manager/error_mode>, which may affect other classes.
4923              
4924             If none of that dissuades you, here's how to do it:
4925              
4926             package Product;
4927              
4928             use Rose::DB::Object:;
4929             our @ISA = qw(Rose::DB::Object);
4930              
4931             __PACKAGE__->make_manager_methods('products');
4932              
4933             Finally, sometimes you don't want or need to use L<make_manager_methods|/make_manager_methods> at all. In fact, this method did not exist in earlier versions of this module. The formerly recommended way to use this class is still perfectly valid: subclass it and then call through to the base class methods.
4934              
4935             package Product::Manager;
4936              
4937             use Rose::DB::Object::Manager;
4938             our @ISA = qw(Rose::DB::Object::Manager);
4939              
4940             sub get_products
4941             {
4942             shift->get_objects(object_class => 'Product', @_);
4943             }
4944              
4945             sub get_products_iterator
4946             {
4947             shift->get_objects_iterator(object_class => 'Product', @_);
4948             }
4949              
4950             sub get_products_count
4951             {
4952             shift->get_objects_count(object_class => 'Product', @_);
4953             }
4954              
4955             sub delete_products
4956             {
4957             shift->delete_objects(object_class => 'Product', @_);
4958             }
4959              
4960             sub update_products
4961             {
4962             shift->update_objects(object_class => 'Product', @_);
4963             }
4964              
4965             Of course, these methods will all look very similar in each L<Rose::DB::Object::Manager>-derived class. Creating these identically structured methods is exactly what L<make_manager_methods|/make_manager_methods> automates for you.
4966              
4967             But sometimes you want to customize these methods, in which case the "longhand" technique above becomes essential. For example, imagine that we want to extend the code in the L<synopsis|/SYNOPSIS>, adding support for a C<with_categories> parameter to the C<get_products()> method.
4968              
4969             Product::Manager->get_products(date_created => '10/21/2001',
4970             with_categories => 1);
4971              
4972             ...
4973              
4974             sub get_products
4975             {
4976             my($class, %args) @_;
4977              
4978             if(delete $args{'with_categories'}) # boolean flag
4979             {
4980             push(@{$args{'with_objects'}}, 'category');
4981             }
4982              
4983             Rose::DB::Object::Manager->get_objects(
4984             %args, object_class => 'Product')
4985             }
4986              
4987             Here we've coerced the caller-friendly C<with_categories> boolean flag parameter into the C<with_objects =E<gt> [ 'category' ]> pair that L<Rose::DB::Object::Manager>'s L<get_objects|/get_objects> method can understand.
4988              
4989             This is the typical evolution of an object manager method. It starts out as being auto-generated by L<make_manager_methods|/make_manager_methods>, then becomes customized as new arguments are added.
4990              
4991             =item B<make_manager_method_from_sql [ NAME =E<gt> SQL | PARAMS ]>
4992              
4993             Create a class method in the calling class that will fetch objects using a custom SQL query. The method created will return a reference to an array of objects or a L<Rose::DB::Object::Iterator> object, depending on whether the C<iterator> parameter is set (see below).
4994              
4995             Pass either a method name and an SQL query string or name/value parameters as arguments. Valid parameters are:
4996              
4997             =over 4
4998              
4999             =item B<iterator BOOL>
5000              
5001             If true, the method created will return a L<Rose::DB::Object::Iterator> object.
5002              
5003             =item B<object_class CLASS>
5004              
5005             The class name of the L<Rose::DB::Object>-derived objects to be fetched. Defaults to the value returned by the L<object_class|/object_class> class method.
5006              
5007             =item B<params ARRAYREF>
5008              
5009             To allow the method that will be created to accept named parameters (name/value pairs) instead of positional parameters, provide a reference to an array of parameter names in the order that they should be passed to the call to L<DBI>'s L<execute|DBI/execute> method.
5010              
5011             =item B<method NAME>
5012              
5013             The name of the method to be created. This parameter is required.
5014              
5015             =item B<prepare_cached BOOL>
5016              
5017             If true, then L<DBI>'s L<prepare_cached|DBI/prepare_cached> method will be used (instead of the L<prepare|DBI/prepare> method) when preparing the SQL statement that will fetch the objects. If omitted, the default value is determined by the L<dbi_prepare_cached|/dbi_prepare_cached> class method.
5018              
5019             =item B<share_db BOOL>
5020              
5021             If true, C<db> will be passed to each L<Rose::DB::Object>-derived object when it is constructed. Defaults to true.
5022              
5023             =item B<sql SQL>
5024              
5025             The SQL query string. This parameter is required.
5026              
5027             =back
5028              
5029             Each column returned by the SQL query must be either a column or method name in C<object_class>. Column names take precedence in the case of a conflict.
5030              
5031             Arguments passed to the created method will be passed to L<DBI>'s L<execute|DBI/execute> method when the query is run. The number of arguments must exactly match the number of placeholders in the SQL query. Positional parameters are required unless the C<params> parameter is used. (See description above.)
5032              
5033             Returns a code reference to the method created.
5034              
5035             Examples:
5036              
5037             package Product::Manager;
5038              
5039             use base 'Rose::DB::Object::Manager';
5040             ...
5041              
5042             # Make method that takes no arguments
5043             __PACKAGE__->make_manager_method_from_sql(get_odd_products =><<"EOF");
5044             SELECT * FROM products WHERE sku % 2 != 0
5045             EOF
5046              
5047             # Make method that takes one positional parameter
5048             __PACKAGE__->make_manager_method_from_sql(get_new_products =><<"EOF");
5049             SELECT * FROM products WHERE release_date > ?
5050             EOF
5051              
5052             # Make method that takes named parameters
5053             __PACKAGE__->make_manager_method_from_sql(
5054             method => 'get_named_products',
5055             params => [ qw(type name) ],
5056             sql => <<"EOF");
5057             SELECT * FROM products WHERE type = ? AND name LIKE ?
5058             EOF
5059              
5060             ...
5061              
5062             $products = Product::Manager->get_odd_products();
5063              
5064             $products = Product::Manager->get_new_products('2005-01-01');
5065              
5066             $products =
5067             Product::Manager->get_named_products(
5068             name => 'Kite%',
5069             type => 'toy');
5070              
5071             # Make method that takes named parameters and returns an iterator
5072             __PACKAGE__->make_manager_method_from_sql(
5073             method => 'get_named_products_iterator',
5074             iterator => 1,
5075             params => [ qw(type name) ],
5076             sql => <<"EOF");
5077             SELECT * FROM products WHERE type = ? AND name LIKE ?
5078             EOF
5079              
5080             $iterator =
5081             Product::Manager->get_named_products_iterator(
5082             name => 'Kite%',
5083             type => 'toy');
5084              
5085             while(my $product = $iterator->next)
5086             {
5087             ... # do something with $product
5088              
5089             $iterator->finish if(...); # finish early?
5090             }
5091              
5092             =item B<normalize_get_objects_args [ARGS]>
5093              
5094             This method takes ARGS in the forms accepted by L<get_objects|/get_objects> (and other similar methods) and normalizes them into name/value pairs. Since L<get_objects|/get_objects> can take arguments in many forms, this method is useful when overriding L<get_objects|/get_objects> in a custom L<Rose::DB::Object::Manager> subclass. Example:
5095              
5096              
5097             package Product::Manager;
5098              
5099             use base 'Rose::DB::Object::Manager';
5100              
5101             use Product;
5102              
5103             sub object_class { 'Product' }
5104             ...
5105              
5106             sub get_products
5107             {
5108             my($class, %args) = shift->normalize_get_objects_args(@_);
5109              
5110             # Detect, extract, and handle custom argument
5111             if(delete $args{'active_only'})
5112             {
5113             push(@{$args{'query'}}, status => 'active');
5114             }
5115              
5116             return $class->get_objects(%args); # call through to normal method
5117             }
5118              
5119             Now all of the following calls will work:
5120              
5121             $products =
5122             Product::Manager->get_products([ type => 'boat' ], sort_by => 'name');
5123              
5124             $products =
5125             Product::Manager->get_products({ name => { like => '%Dog%' } });
5126              
5127             $products =
5128             Product::Manager->get_products([ id => { gt => 123 } ], active_only => 1);
5129              
5130             =item B<object_class>
5131              
5132             Returns the class name of the L<Rose::DB::Object>-derived objects to be managed by this class. Override this method in your subclass. The default implementation returns undef.
5133              
5134             =item B<perl_class_definition>
5135              
5136             Attempts to create the Perl source code that is equivalent to the current class. This works best for classes created via L<Rose::DB::Object::Metadata>'s L<make_manager_class|Rose::DB::Object::Metadata/make_manager_class> method, but it will also work most of the time for classes whose methods were created using L<make_manager_methods|/make_manager_methods>.
5137              
5138             The Perl code is returned as a string. Here's an example:
5139              
5140             package My::Product::Manager;
5141              
5142             use My::Product;
5143              
5144             use Rose::DB::Object::Manager;
5145             our @ISA = qw(Rose::DB::Object::Manager);
5146              
5147             sub object_class { 'My::Product' }
5148              
5149             __PACKAGE__->make_manager_methods('products');
5150              
5151             1;
5152              
5153             =item B<update_objects [PARAMS]>
5154              
5155             Update rows in a table fronted by a L<Rose::DB::Object>-derived class based on PARAMS, where PARAMS are name/value pairs. Returns the number of rows updated, or undef if there was an error.
5156              
5157             Valid parameters are:
5158              
5159             =over 4
5160              
5161             =item B<all BOOL>
5162              
5163             If set to a true value, this parameter indicates an explicit request to update all rows in the table. If both the C<all> and the C<where> parameters are passed, a fatal error will occur.
5164              
5165             =item B<db DB>
5166              
5167             A L<Rose::DB>-derived object used to access the database. If omitted, one will be created by calling the L<init_db|Rose::DB::Object/init_db> method of the C<object_class>.
5168              
5169             =item B<object_class CLASS>
5170              
5171             The class name of the L<Rose::DB::Object>-derived class that fronts the table whose rows will to be updated. This parameter is required; a fatal error will occur if it is omitted. Defaults to the value returned by the L<object_class|/object_class> class method.
5172              
5173             =item B<set PARAMS>
5174              
5175             The names and values of the columns to be updated. PARAMS should be a reference to a hash. Each key of the hash should be a column name or column get/set method name. If a value is a simple scalar, then it is passed through the get/set method that services the column before being incorporated into the SQL query.
5176              
5177             If a value is a reference to a scalar, then it is dereferenced and incorporated into the SQL query as-is.
5178              
5179             If a value is a reference to a hash, then it must contain a single key named "sql" and a corresponding value that will be incorporated into the SQL query as-is.
5180              
5181             Example:
5182              
5183             $num_rows_updated =
5184             Product::Manager->update_products(
5185             set =>
5186             {
5187             end_date => DateTime->now,
5188             region_num => { sql => 'region_num * -1' }
5189             count => \q(count + 1),
5190             status => 'defunct',
5191             },
5192             where =>
5193             [
5194             status => [ 'stale', 'old' ],
5195             name => { like => 'Wax%' }
5196             or =>
5197             [
5198             start_date => { gt => '2008-12-30' },
5199             end_date => { gt => 'now' },
5200             ],
5201             ]);
5202              
5203             The call above would execute an SQL statement something like the one shown below (depending on the database vendor, and assuming the current date was September 20th, 2005):
5204              
5205             UPDATE products SET
5206             end_date = '2005-09-20',
5207             region_num = region_num * -1,
5208             count = count + 1,
5209             status = 'defunct'
5210             WHERE
5211             status IN ('stale', 'old') AND
5212             name LIKE 'Wax%' AND
5213             (
5214             start_date > '2008-12-30' OR
5215             end_date > '2005-09-20'
5216             )
5217              
5218             =item B<where PARAMS>
5219              
5220             The query parameters, passed as a reference to an array of name/value pairs. These PARAMS are used to formulate the "where" clause of the SQL query that is used to update the rows in the table. Arbitrarily nested boolean logic is supported.
5221              
5222             For the complete list of valid parameter names and values, see the documentation for the C<query> parameter of the L<build_select|Rose::DB::Object::QueryBuilder/build_select> function in the L<Rose::DB::Object::QueryBuilder> module.
5223              
5224             If this parameter is omitted, this method will refuse to update all rows in the table and a fatal error will occur. To update all rows in a table, you must pass the C<all> parameter with a true value. If both the C<all> and the C<where> parameters are passed, a fatal error will occur.
5225              
5226             =back
5227              
5228             =item B<strict_ops [BOOL]>
5229              
5230             Get or set a boolean value that indicates whether using a comparison operator in the C<query> that is not listed in the L<Rose::DB::Object::QueryBuilder> documentation will cause a fatal error. The default value is false.
5231              
5232             =back
5233              
5234             =head1 SUPPORT
5235              
5236             For an informal overview of L<Rose::DB::Object>, including L<Rose::DB::Object::Manager>, consult the L<Rose::DB::Object::Tutorial>.
5237              
5238             perldoc Rose::DB::Object::Tutorial
5239              
5240             Any L<Rose::DB::Object::Manager> questions or problems can be posted to the L<Rose::DB::Object> mailing list. To subscribe to the list or view the archives, go here:
5241              
5242             L<http://groups.google.com/group/rose-db-object>
5243              
5244             Although the mailing list is the preferred support mechanism, you can also email the author (see below) or file bugs using the CPAN bug tracking system:
5245              
5246             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Rose-DB-Object>
5247              
5248             There's also a wiki and other resources linked from the Rose project home page:
5249              
5250             L<http://rosecode.org>
5251              
5252             =head1 AUTHOR
5253              
5254             John C. Siracusa (siracusa@gmail.com)
5255              
5256             =head1 LICENSE
5257              
5258             Copyright (c) 2010 by John C. Siracusa. All rights reserved. This program is
5259             free software; you can redistribute it and/or modify it under the same terms
5260             as Perl itself.