[svn-upgrade] Integrating new upstream version, libdbix-class-perl (0.08118)
Jonathan Yu
14 years ago
0 | 0 | Revision history for DBIx::Class |
1 | ||
2 | 0.08118 2010-02-08 11:53:00 (UTC) | |
3 | - Fix a bug causing UTF8 columns not to be decoded (RT#54395) | |
4 | - Fix bug in One->Many->One prefetch-collapse handling (RT#54039) | |
5 | - Cleanup handling of relationship accessor types | |
1 | 6 | |
2 | 7 | 0.08117 2010-02-05 17:10:00 (UTC) |
3 | 8 | - Perl 5.8.1 is now the minimum supported version |
442 | 442 | t/prefetch/incomplete.t |
443 | 443 | t/prefetch/join_type.t |
444 | 444 | t/prefetch/multiple_hasmany.t |
445 | t/prefetch/one_to_many_to_one.t | |
445 | 446 | t/prefetch/standard.t |
446 | 447 | t/prefetch/via_search_related.t |
447 | 448 | t/prefetch/with_limit.t |
451 | 452 | t/relationship/update_or_create_multi.t |
452 | 453 | t/relationship/update_or_create_single.t |
453 | 454 | t/resultset/as_query.t |
455 | t/resultset/as_subselect_rs.t | |
454 | 456 | t/resultset/is_paged.t |
455 | 457 | t/resultset/nulls_only.t |
456 | 458 | t/resultset/plus_select.t |
55 | 55 | MailingList: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class |
56 | 56 | license: http://dev.perl.org/licenses/ |
57 | 57 | repository: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/ |
58 | version: 0.08117 | |
58 | version: 0.08118 |
78 | 78 | $self->throw_exception("inflate_column needs attr hashref") |
79 | 79 | unless ref $attrs eq 'HASH'; |
80 | 80 | $self->column_info($col)->{_inflate_info} = $attrs; |
81 | $self->mk_group_accessors('inflated_column' => [$self->column_info($col)->{accessor} || $col, $col]); | |
81 | my $acc = $self->column_info($col)->{accessor}; | |
82 | $self->mk_group_accessors('inflated_column' => [ (defined $acc ? $acc : $col), $col]); | |
82 | 83 | return 1; |
83 | 84 | } |
84 | 85 |
28 | 28 | =back |
29 | 29 | |
30 | 30 | __PACKAGE__->add_relationship('relname', 'Foreign::Class', $cond, $attrs); |
31 | ||
32 | =head3 condition | |
31 | 33 | |
32 | 34 | The condition needs to be an L<SQL::Abstract>-style representation of the |
33 | 35 | join between the tables. When resolving the condition for use in a C<JOIN>, |
66 | 68 | To add an C<OR>ed condition, use an arrayref of hashrefs. See the |
67 | 69 | L<SQL::Abstract> documentation for more details. |
68 | 70 | |
69 | In addition to the | |
70 | L<standard ResultSet attributes|DBIx::Class::ResultSet/ATTRIBUTES>, | |
71 | the following attributes are also valid: | |
71 | =head3 attributes | |
72 | ||
73 | The L<standard ResultSet attributes|DBIx::Class::ResultSet/ATTRIBUTES> may | |
74 | be used as relationship attributes. In particular, the 'where' attribute is | |
75 | useful for filtering relationships: | |
76 | ||
77 | __PACKAGE__->has_many( 'valid_users', 'MyApp::Schema::User', | |
78 | { 'foreign.user_id' => 'self.user_id' }, | |
79 | { where => { valid => 1 } } | |
80 | ); | |
81 | ||
82 | The following attributes are also valid: | |
72 | 83 | |
73 | 84 | =over 4 |
74 | 85 | |
194 | 205 | if ($cond eq $DBIx::Class::ResultSource::UNRESOLVABLE_CONDITION) { |
195 | 206 | my $reverse = $source->reverse_relationship_info($rel); |
196 | 207 | foreach my $rev_rel (keys %$reverse) { |
197 | if ($reverse->{$rev_rel}{attrs}{accessor} eq 'multi') { | |
208 | if ($reverse->{$rev_rel}{attrs}{accessor} && $reverse->{$rev_rel}{attrs}{accessor} eq 'multi') { | |
198 | 209 | $attrs->{related_objects}{$rev_rel} = [ $self ]; |
199 | 210 | Scalar::Util::weaken($attrs->{related_object}{$rev_rel}[0]); |
200 | 211 | } else { |
38 | 38 | my @cascade = grep { $rels{$_}{attrs}{cascade_update} } keys %rels; |
39 | 39 | foreach my $rel (@cascade) { |
40 | 40 | next if ( |
41 | $rels{$rel}{attrs}{accessor} | |
42 | && | |
41 | 43 | $rels{$rel}{attrs}{accessor} eq 'single' |
42 | && !exists($self->{_relationship_data}{$rel}) | |
44 | && | |
45 | !exists($self->{_relationship_data}{$rel}) | |
43 | 46 | ); |
44 | 47 | $_->update for grep defined, $self->$rel; |
45 | 48 | } |
2501 | 2501 | ->relname_to_table_alias($rel, $join_count); |
2502 | 2502 | |
2503 | 2503 | # since this is search_related, and we already slid the select window inwards |
2504 | # (the select/as attrs were deleted in the beginning), we need to flip all | |
2504 | # (the select/as attrs were deleted in the beginning), we need to flip all | |
2505 | 2505 | # left joins to inner, so we get the expected results |
2506 | 2506 | # read the comment on top of the actual function to see what this does |
2507 | 2507 | $attrs->{from} = $rsrc->schema->storage->_straight_join_to_node ($attrs->{from}, $alias); |
2585 | 2585 | my ($self) = @_; |
2586 | 2586 | |
2587 | 2587 | return ($self->{attrs} || {})->{alias} || 'me'; |
2588 | } | |
2589 | ||
2590 | =head2 as_subselect_rs | |
2591 | ||
2592 | =over 4 | |
2593 | ||
2594 | =item Arguments: none | |
2595 | ||
2596 | =item Return Value: $resultset | |
2597 | ||
2598 | =back | |
2599 | ||
2600 | Act as a barrier to SQL symbols. The resultset provided will be made into a | |
2601 | "virtual view" by including it as a subquery within the from clause. From this | |
2602 | point on, any joined tables are inaccessible to ->search on the resultset (as if | |
2603 | it were simply where-filtered without joins). For example: | |
2604 | ||
2605 | my $rs = $schema->resultset('Bar')->search({'x.name' => 'abc'},{ join => 'x' }); | |
2606 | ||
2607 | # 'x' now pollutes the query namespace | |
2608 | ||
2609 | # So the following works as expected | |
2610 | my $ok_rs = $rs->search({'x.other' => 1}); | |
2611 | ||
2612 | # But this doesn't: instead of finding a 'Bar' related to two x rows (abc and | |
2613 | # def) we look for one row with contradictory terms and join in another table | |
2614 | # (aliased 'x_2') which we never use | |
2615 | my $broken_rs = $rs->search({'x.name' => 'def'}); | |
2616 | ||
2617 | my $rs2 = $rs->as_subselect_rs; | |
2618 | ||
2619 | # doesn't work - 'x' is no longer accessible in $rs2, having been sealed away | |
2620 | my $not_joined_rs = $rs2->search({'x.other' => 1}); | |
2621 | ||
2622 | # works as expected: finds a 'table' row related to two x rows (abc and def) | |
2623 | my $correctly_joined_rs = $rs2->search({'x.name' => 'def'}); | |
2624 | ||
2625 | Another example of when one might use this would be to select a subset of | |
2626 | columns in a group by clause: | |
2627 | ||
2628 | my $rs = $schema->resultset('Bar')->search(undef, { | |
2629 | group_by => [qw{ id foo_id baz_id }], | |
2630 | })->as_subselect_rs->search(undef, { | |
2631 | columns => [qw{ id foo_id }] | |
2632 | }); | |
2633 | ||
2634 | In the above example normally columns would have to be equal to the group by, | |
2635 | but because we isolated the group by into a subselect the above works. | |
2636 | ||
2637 | =cut | |
2638 | ||
2639 | sub as_subselect_rs { | |
2640 | my $self = shift; | |
2641 | ||
2642 | return $self->result_source->resultset->search( undef, { | |
2643 | alias => $self->current_source_alias, | |
2644 | from => [{ | |
2645 | $self->current_source_alias => $self->as_query, | |
2646 | -alias => $self->current_source_alias, | |
2647 | -source_handle => $self->result_source->handle, | |
2648 | }] | |
2649 | }); | |
2588 | 2650 | } |
2589 | 2651 | |
2590 | 2652 | # This code is called by search_related, and makes sure there |
1187 | 1187 | return $found; |
1188 | 1188 | } |
1189 | 1189 | |
1190 | sub resolve_join { | |
1191 | carp 'resolve_join is a private method, stop calling it'; | |
1192 | my $self = shift; | |
1193 | $self->_resolve_join (@_); | |
1194 | } | |
1195 | ||
1196 | 1190 | # Returns the {from} structure used to express JOIN conditions |
1197 | 1191 | sub _resolve_join { |
1198 | 1192 | my ($self, $join, $alias, $seen, $jpath, $parent_force_left) = @_; |
1261 | 1255 | : $rel_info->{attrs}{join_type} |
1262 | 1256 | , |
1263 | 1257 | -join_path => [@$jpath, { $join => $as } ], |
1264 | -is_single => (List::Util::first { $rel_info->{attrs}{accessor} eq $_ } (qw/single filter/) ), | |
1258 | -is_single => ( | |
1259 | $rel_info->{attrs}{accessor} | |
1260 | && | |
1261 | List::Util::first { $rel_info->{attrs}{accessor} eq $_ } (qw/single filter/) | |
1262 | ), | |
1265 | 1263 | -alias => $as, |
1266 | 1264 | -relation_chain_depth => $seen->{-relation_chain_depth} || 0, |
1267 | 1265 | }, |
1372 | 1370 | } |
1373 | 1371 | } |
1374 | 1372 | |
1375 | # Legacy code, needs to go entirely away (fully replaced by _resolve_prefetch) | |
1376 | sub resolve_prefetch { | |
1377 | carp 'resolve_prefetch is a private method, stop calling it'; | |
1378 | ||
1379 | my ($self, $pre, $alias, $seen, $order, $collapse) = @_; | |
1380 | $seen ||= {}; | |
1381 | if( ref $pre eq 'ARRAY' ) { | |
1373 | ||
1374 | # Accepts one or more relationships for the current source and returns an | |
1375 | # array of column names for each of those relationships. Column names are | |
1376 | # prefixed relative to the current source, in accordance with where they appear | |
1377 | # in the supplied relationships. | |
1378 | ||
1379 | sub _resolve_prefetch { | |
1380 | my ($self, $pre, $alias, $alias_map, $order, $collapse, $pref_path) = @_; | |
1381 | $pref_path ||= []; | |
1382 | ||
1383 | if (not defined $pre) { | |
1384 | return (); | |
1385 | } | |
1386 | elsif( ref $pre eq 'ARRAY' ) { | |
1382 | 1387 | return |
1383 | map { $self->resolve_prefetch( $_, $alias, $seen, $order, $collapse ) } | |
1388 | map { $self->_resolve_prefetch( $_, $alias, $alias_map, $order, $collapse, [ @$pref_path ] ) } | |
1384 | 1389 | @$pre; |
1385 | 1390 | } |
1386 | 1391 | elsif( ref $pre eq 'HASH' ) { |
1387 | 1392 | my @ret = |
1388 | 1393 | map { |
1389 | $self->resolve_prefetch($_, $alias, $seen, $order, $collapse), | |
1390 | $self->related_source($_)->resolve_prefetch( | |
1391 | $pre->{$_}, "${alias}.$_", $seen, $order, $collapse) | |
1394 | $self->_resolve_prefetch($_, $alias, $alias_map, $order, $collapse, [ @$pref_path ] ), | |
1395 | $self->related_source($_)->_resolve_prefetch( | |
1396 | $pre->{$_}, "${alias}.$_", $alias_map, $order, $collapse, [ @$pref_path, $_] ) | |
1392 | 1397 | } keys %$pre; |
1393 | 1398 | return @ret; |
1394 | 1399 | } |
1397 | 1402 | "don't know how to resolve prefetch reftype ".ref($pre)); |
1398 | 1403 | } |
1399 | 1404 | else { |
1400 | my $count = ++$seen->{$pre}; | |
1401 | my $as = ($count > 1 ? "${pre}_${count}" : $pre); | |
1405 | my $p = $alias_map; | |
1406 | $p = $p->{$_} for (@$pref_path, $pre); | |
1407 | ||
1408 | $self->throw_exception ( | |
1409 | "Unable to resolve prefetch '$pre' - join alias map does not contain an entry for path: " | |
1410 | . join (' -> ', @$pref_path, $pre) | |
1411 | ) if (ref $p->{-join_aliases} ne 'ARRAY' or not @{$p->{-join_aliases}} ); | |
1412 | ||
1413 | my $as = shift @{$p->{-join_aliases}}; | |
1414 | ||
1402 | 1415 | my $rel_info = $self->relationship_info( $pre ); |
1403 | 1416 | $self->throw_exception( $self->name . " has no such relationship '$pre'" ) |
1404 | 1417 | unless $rel_info; |
1405 | 1418 | my $as_prefix = ($alias =~ /^.*?\.(.+)$/ ? $1.'.' : ''); |
1406 | 1419 | my $rel_source = $self->related_source($pre); |
1407 | 1420 | |
1408 | if (exists $rel_info->{attrs}{accessor} | |
1409 | && $rel_info->{attrs}{accessor} eq 'multi') { | |
1421 | if ($rel_info->{attrs}{accessor} && $rel_info->{attrs}{accessor} eq 'multi') { | |
1410 | 1422 | $self->throw_exception( |
1411 | 1423 | "Can't prefetch has_many ${pre} (join cond too complex)") |
1412 | 1424 | unless ref($rel_info->{cond}) eq 'HASH'; |
1433 | 1445 | keys %{$rel_info->{cond}}; |
1434 | 1446 | my @ord = (ref($rel_info->{attrs}{order_by}) eq 'ARRAY' |
1435 | 1447 | ? @{$rel_info->{attrs}{order_by}} |
1436 | : (defined $rel_info->{attrs}{order_by} | |
1437 | ? ($rel_info->{attrs}{order_by}) | |
1438 | : ())); | |
1439 | push(@$order, map { "${as}.$_" } (@key, @ord)); | |
1440 | } | |
1441 | ||
1442 | return map { [ "${as}.$_", "${as_prefix}${pre}.$_", ] } | |
1443 | $rel_source->columns; | |
1444 | } | |
1445 | } | |
1446 | ||
1447 | # Accepts one or more relationships for the current source and returns an | |
1448 | # array of column names for each of those relationships. Column names are | |
1449 | # prefixed relative to the current source, in accordance with where they appear | |
1450 | # in the supplied relationships. | |
1451 | ||
1452 | sub _resolve_prefetch { | |
1453 | my ($self, $pre, $alias, $alias_map, $order, $collapse, $pref_path) = @_; | |
1454 | $pref_path ||= []; | |
1455 | ||
1456 | if (not defined $pre) { | |
1457 | return (); | |
1458 | } | |
1459 | elsif( ref $pre eq 'ARRAY' ) { | |
1460 | return | |
1461 | map { $self->_resolve_prefetch( $_, $alias, $alias_map, $order, $collapse, [ @$pref_path ] ) } | |
1462 | @$pre; | |
1463 | } | |
1464 | elsif( ref $pre eq 'HASH' ) { | |
1465 | my @ret = | |
1466 | map { | |
1467 | $self->_resolve_prefetch($_, $alias, $alias_map, $order, $collapse, [ @$pref_path ] ), | |
1468 | $self->related_source($_)->_resolve_prefetch( | |
1469 | $pre->{$_}, "${alias}.$_", $alias_map, $order, $collapse, [ @$pref_path, $_] ) | |
1470 | } keys %$pre; | |
1471 | return @ret; | |
1472 | } | |
1473 | elsif( ref $pre ) { | |
1474 | $self->throw_exception( | |
1475 | "don't know how to resolve prefetch reftype ".ref($pre)); | |
1476 | } | |
1477 | else { | |
1478 | my $p = $alias_map; | |
1479 | $p = $p->{$_} for (@$pref_path, $pre); | |
1480 | ||
1481 | $self->throw_exception ( | |
1482 | "Unable to resolve prefetch '$pre' - join alias map does not contain an entry for path: " | |
1483 | . join (' -> ', @$pref_path, $pre) | |
1484 | ) if (ref $p->{-join_aliases} ne 'ARRAY' or not @{$p->{-join_aliases}} ); | |
1485 | ||
1486 | my $as = shift @{$p->{-join_aliases}}; | |
1487 | ||
1488 | my $rel_info = $self->relationship_info( $pre ); | |
1489 | $self->throw_exception( $self->name . " has no such relationship '$pre'" ) | |
1490 | unless $rel_info; | |
1491 | my $as_prefix = ($alias =~ /^.*?\.(.+)$/ ? $1.'.' : ''); | |
1492 | my $rel_source = $self->related_source($pre); | |
1493 | ||
1494 | if (exists $rel_info->{attrs}{accessor} | |
1495 | && $rel_info->{attrs}{accessor} eq 'multi') { | |
1496 | $self->throw_exception( | |
1497 | "Can't prefetch has_many ${pre} (join cond too complex)") | |
1498 | unless ref($rel_info->{cond}) eq 'HASH'; | |
1499 | my $dots = @{[$as_prefix =~ m/\./g]} + 1; # +1 to match the ".${as_prefix}" | |
1500 | if (my ($fail) = grep { @{[$_ =~ m/\./g]} == $dots } | |
1501 | keys %{$collapse}) { | |
1502 | my ($last) = ($fail =~ /([^\.]+)$/); | |
1503 | carp ( | |
1504 | "Prefetching multiple has_many rels ${last} and ${pre} " | |
1505 | .(length($as_prefix) | |
1506 | ? "at the same level (${as_prefix}) " | |
1507 | : "at top level " | |
1508 | ) | |
1509 | . 'will explode the number of row objects retrievable via ->next or ->all. ' | |
1510 | . 'Use at your own risk.' | |
1511 | ); | |
1512 | } | |
1513 | #my @col = map { (/^self\.(.+)$/ ? ("${as_prefix}.$1") : ()); } | |
1514 | # values %{$rel_info->{cond}}; | |
1515 | $collapse->{".${as_prefix}${pre}"} = [ $rel_source->primary_columns ]; | |
1516 | # action at a distance. prepending the '.' allows simpler code | |
1517 | # in ResultSet->_collapse_result | |
1518 | my @key = map { (/^foreign\.(.+)$/ ? ($1) : ()); } | |
1519 | keys %{$rel_info->{cond}}; | |
1520 | my @ord = (ref($rel_info->{attrs}{order_by}) eq 'ARRAY' | |
1521 | ? @{$rel_info->{attrs}{order_by}} | |
1522 | : (defined $rel_info->{attrs}{order_by} | |
1448 | ||
1449 | : (defined $rel_info->{attrs}{order_by} | |
1523 | 1450 | ? ($rel_info->{attrs}{order_by}) |
1524 | 1451 | : ())); |
1525 | 1452 | push(@$order, map { "${as}.$_" } (@key, @ord)); |
170 | 170 | $new->throw_exception("Can't do multi-create without result source") |
171 | 171 | unless $source; |
172 | 172 | my $info = $source->relationship_info($key); |
173 | if ($info && $info->{attrs}{accessor} | |
174 | && $info->{attrs}{accessor} eq 'single') | |
175 | { | |
173 | my $acc_type = $info->{attrs}{accessor} || ''; | |
174 | if ($acc_type eq 'single') { | |
176 | 175 | my $rel_obj = delete $attrs->{$key}; |
177 | 176 | if(!Scalar::Util::blessed($rel_obj)) { |
178 | 177 | $rel_obj = $new->__new_related_find_or_new_helper($key, $rel_obj); |
187 | 186 | |
188 | 187 | $related->{$key} = $rel_obj; |
189 | 188 | next; |
190 | } elsif ($info && $info->{attrs}{accessor} | |
191 | && $info->{attrs}{accessor} eq 'multi' | |
192 | && ref $attrs->{$key} eq 'ARRAY') { | |
189 | } | |
190 | elsif ($acc_type eq 'multi' && ref $attrs->{$key} eq 'ARRAY' ) { | |
193 | 191 | my $others = delete $attrs->{$key}; |
194 | 192 | my $total = @$others; |
195 | 193 | my @objects; |
209 | 207 | } |
210 | 208 | $related->{$key} = \@objects; |
211 | 209 | next; |
212 | } elsif ($info && $info->{attrs}{accessor} | |
213 | && $info->{attrs}{accessor} eq 'filter') | |
214 | { | |
210 | } | |
211 | elsif ($acc_type eq 'filter') { | |
215 | 212 | ## 'filter' should disappear and get merged in with 'single' above! |
216 | 213 | my $rel_obj = delete $attrs->{$key}; |
217 | 214 | if(!Scalar::Util::blessed($rel_obj)) { |
762 | 759 | for my $col (keys %loaded_colinfo) { |
763 | 760 | if (exists $loaded_colinfo{$col}{accessor}) { |
764 | 761 | my $acc = $loaded_colinfo{$col}{accessor}; |
765 | if (defined $acc) { | |
766 | $inflated{$col} = $self->$acc; | |
767 | } | |
762 | $inflated{$col} = $self->$acc if defined $acc; | |
768 | 763 | } |
769 | 764 | else { |
770 | 765 | $inflated{$col} = $self->$col; |
916 | 911 | foreach my $key (keys %$upd) { |
917 | 912 | if (ref $upd->{$key}) { |
918 | 913 | my $info = $self->relationship_info($key); |
919 | if ($info && $info->{attrs}{accessor} | |
920 | && $info->{attrs}{accessor} eq 'single') | |
921 | { | |
914 | my $acc_type = $info->{attrs}{accessor} || ''; | |
915 | if ($acc_type eq 'single') { | |
922 | 916 | my $rel = delete $upd->{$key}; |
923 | 917 | $self->set_from_related($key => $rel); |
924 | 918 | $self->{_relationship_data}{$key} = $rel; |
925 | } elsif ($info && $info->{attrs}{accessor} | |
926 | && $info->{attrs}{accessor} eq 'multi') { | |
927 | $self->throw_exception( | |
928 | "Recursive update is not supported over relationships of type multi ($key)" | |
929 | ); | |
930 | 919 | } |
931 | elsif ($self->has_column($key) | |
932 | && exists $self->column_info($key)->{_inflate_info}) | |
933 | { | |
920 | elsif ($acc_type eq 'multi') { | |
921 | $self->throw_exception( | |
922 | "Recursive update is not supported over relationships of type '$acc_type' ($key)" | |
923 | ); | |
924 | } | |
925 | elsif ($self->has_column($key) && exists $self->column_info($key)->{_inflate_info}) { | |
934 | 926 | $self->set_inflated_column($key, delete $upd->{$key}); |
935 | 927 | } |
936 | 928 | } |
1069 | 1061 | my ($source_handle) = $source; |
1070 | 1062 | |
1071 | 1063 | if ($source->isa('DBIx::Class::ResultSourceHandle')) { |
1072 | $source = $source_handle->resolve | |
1073 | } else { | |
1074 | $source_handle = $source->handle | |
1064 | $source = $source_handle->resolve | |
1065 | } | |
1066 | else { | |
1067 | $source_handle = $source->handle | |
1075 | 1068 | } |
1076 | 1069 | |
1077 | 1070 | my $new = { |
1080 | 1073 | }; |
1081 | 1074 | bless $new, (ref $class || $class); |
1082 | 1075 | |
1083 | my $schema; | |
1084 | 1076 | foreach my $pre (keys %{$prefetch||{}}) { |
1085 | my $pre_val = $prefetch->{$pre}; | |
1086 | my $pre_source = $source->related_source($pre); | |
1087 | $class->throw_exception("Can't prefetch non-existent relationship ${pre}") | |
1088 | unless $pre_source; | |
1089 | if (ref($pre_val->[0]) eq 'ARRAY') { # multi | |
1090 | my @pre_objects; | |
1091 | ||
1092 | for my $me_pref (@$pre_val) { | |
1093 | ||
1077 | ||
1078 | my $pre_source = $source->related_source($pre) | |
1079 | or $class->throw_exception("Can't prefetch non-existent relationship ${pre}"); | |
1080 | ||
1081 | my $accessor = $source->relationship_info($pre)->{attrs}{accessor} | |
1082 | or $class->throw_exception("No accessor for prefetched $pre"); | |
1083 | ||
1084 | my @pre_vals; | |
1085 | if (ref $prefetch->{$pre}[0] eq 'ARRAY') { | |
1086 | @pre_vals = @{$prefetch->{$pre}}; | |
1087 | } | |
1088 | elsif ($accessor eq 'multi') { | |
1089 | $class->throw_exception("Implicit prefetch (via select/columns) not supported with accessor 'multi'"); | |
1090 | } | |
1091 | else { | |
1092 | @pre_vals = $prefetch->{$pre}; | |
1093 | } | |
1094 | ||
1095 | my @pre_objects; | |
1096 | for my $me_pref (@pre_vals) { | |
1097 | ||
1098 | # FIXME - this should not be necessary | |
1094 | 1099 | # the collapser currently *could* return bogus elements with all |
1095 | 1100 | # columns set to undef |
1096 | 1101 | my $has_def; |
1105 | 1110 | push @pre_objects, $pre_source->result_class->inflate_result( |
1106 | 1111 | $pre_source, @$me_pref |
1107 | 1112 | ); |
1108 | } | |
1109 | ||
1110 | $new->related_resultset($pre)->set_cache(\@pre_objects); | |
1111 | } elsif (defined $pre_val->[0]) { | |
1112 | my $fetched; | |
1113 | unless ($pre_source->primary_columns == grep { exists $pre_val->[0]{$_} | |
1114 | and !defined $pre_val->[0]{$_} } $pre_source->primary_columns) | |
1115 | { | |
1116 | $fetched = $pre_source->result_class->inflate_result( | |
1117 | $pre_source, @{$pre_val}); | |
1118 | } | |
1119 | my $accessor = $source->relationship_info($pre)->{attrs}{accessor}; | |
1120 | $class->throw_exception("No accessor for prefetched $pre") | |
1121 | unless defined $accessor; | |
1122 | if ($accessor eq 'single') { | |
1123 | $new->{_relationship_data}{$pre} = $fetched; | |
1124 | } elsif ($accessor eq 'filter') { | |
1125 | $new->{_inflated_column}{$pre} = $fetched; | |
1126 | } else { | |
1127 | $class->throw_exception("Implicit prefetch (via select/columns) not supported with accessor '$accessor'"); | |
1128 | } | |
1129 | $new->related_resultset($pre)->set_cache([ $fetched ]); | |
1130 | } | |
1113 | } | |
1114 | ||
1115 | if ($accessor eq 'single') { | |
1116 | $new->{_relationship_data}{$pre} = $pre_objects[0]; | |
1117 | } | |
1118 | elsif ($accessor eq 'filter') { | |
1119 | $new->{_inflated_column}{$pre} = $pre_objects[0]; | |
1120 | } | |
1121 | ||
1122 | $new->related_resultset($pre)->set_cache(\@pre_objects); | |
1131 | 1123 | } |
1132 | 1124 | |
1133 | 1125 | $new->in_storage (1); |
1 | 1 | use strict; |
2 | 2 | use warnings; |
3 | 3 | use base qw/DBIx::Class/; |
4 | use utf8; | |
5 | 4 | |
6 | 5 | __PACKAGE__->mk_classdata( '_utf8_columns' ); |
7 | 6 | |
113 | 112 | |
114 | 113 | # override this if you want to force everything to be encoded/decoded |
115 | 114 | sub _is_utf8_column { |
116 | return (shift->utf8_columns || {})->{shift}; | |
115 | return (shift->utf8_columns || {})->{shift @_}; | |
117 | 116 | } |
118 | 117 | |
119 | 118 | =head1 AUTHORS |
24 | 24 | # Always remember to do all digits for the version even if they're 0 |
25 | 25 | # i.e. first release of 0.XX *must* be 0.XX000. This avoids fBSD ports |
26 | 26 | # brain damage and presumably various other packaging systems too |
27 | $VERSION = '0.08117'; | |
27 | $VERSION = '0.08118'; | |
28 | 28 | |
29 | 29 | $VERSION = eval $VERSION; # numify for warning-free dev releases |
30 | 30 |
420 | 420 | |
421 | 421 | # make sure we got rid of the compat shims |
422 | 422 | SKIP: { |
423 | skip "Remove in 0.09", 5 if $DBIx::Class::VERSION < 0.09; | |
424 | ||
425 | for (qw/compare_relationship_keys pk_depends_on resolve_condition resolve_join resolve_prefetch/) { | |
423 | skip "Remove in 0.082", 3 if $DBIx::Class::VERSION < 0.082; | |
424 | ||
425 | for (qw/compare_relationship_keys pk_depends_on resolve_condition/) { | |
426 | 426 | ok (! DBIx::Class::ResultSource->can ($_), "$_ no longer provided by DBIx::Class::ResultSource"); |
427 | 427 | } |
428 | 428 | } |
4 | 4 | use Test::Warn; |
5 | 5 | use lib qw(t/lib); |
6 | 6 | use DBICTest; |
7 | use utf8; | |
8 | 7 | |
9 | 8 | warning_like ( |
10 | 9 | sub { |
27 | 26 | DBICTest::Schema::CD->utf8_columns('title'); |
28 | 27 | Class::C3->reinitialize(); |
29 | 28 | |
30 | my $cd = $schema->resultset('CD')->create( { artist => 1, title => 'øni', year => '2048' } ); | |
31 | my $utf8_char = 'uniuni'; | |
32 | ||
29 | my $cd = $schema->resultset('CD')->create( { artist => 1, title => "weird\x{466}stuff", year => '2048' } ); | |
33 | 30 | |
34 | 31 | ok( utf8::is_utf8( $cd->title ), 'got title with utf8 flag' ); |
32 | ok(! utf8::is_utf8( $cd->{_column_data}{title} ), 'store title without utf8' ); | |
33 | ||
35 | 34 | ok(! utf8::is_utf8( $cd->year ), 'got year without utf8 flag' ); |
35 | ok(! utf8::is_utf8( $cd->{_column_data}{year} ), 'store year without utf8' ); | |
36 | 36 | |
37 | utf8::decode($utf8_char); | |
38 | $cd->title($utf8_char); | |
37 | $cd->title('nonunicode'); | |
38 | ok(! utf8::is_utf8( $cd->title ), 'got title without utf8 flag' ); | |
39 | 39 | ok(! utf8::is_utf8( $cd->{_column_data}{title} ), 'store utf8-less chars' ); |
40 | 40 | |
41 | 41 |
71 | 71 | ], |
72 | 72 | }); |
73 | 73 | }, |
74 | qr/Recursive update is not supported over relationships of type multi/, | |
74 | qr/Recursive update is not supported over relationships of type 'multi'/, | |
75 | 75 | 'create via update of multi relationships throws an exception' |
76 | 76 | ); |
77 | 77 |
0 | use strict; | |
1 | use warnings; | |
2 | ||
3 | use Test::More; | |
4 | use Test::Exception; | |
5 | ||
6 | use lib qw(t/lib); | |
7 | use DBICTest; | |
8 | ||
9 | my $schema = DBICTest->init_schema(); | |
10 | ||
11 | my $artist = $schema->resultset ('Artist')->find ({artistid => 1}); | |
12 | is ($artist->cds->count, 3, 'Correct number of CDs'); | |
13 | is ($artist->cds->search_related ('genre')->count, 1, 'Only one of the cds has a genre'); | |
14 | ||
15 | my $queries = 0; | |
16 | my $orig_cb = $schema->storage->debugcb; | |
17 | $schema->storage->debugcb(sub { $queries++ }); | |
18 | $schema->storage->debug(1); | |
19 | ||
20 | ||
21 | my $pref = $schema->resultset ('Artist') | |
22 | ->search ({ 'me.artistid' => $artist->id }, { prefetch => { cds => 'genre' } }) | |
23 | ->next; | |
24 | ||
25 | is ($pref->cds->count, 3, 'Correct number of CDs prefetched'); | |
26 | is ($pref->cds->search_related ('genre')->count, 1, 'Only one of the prefetched cds has a prefetched genre'); | |
27 | ||
28 | ||
29 | is ($queries, 1, 'All happened within one query only'); | |
30 | $schema->storage->debugcb($orig_cb); | |
31 | $schema->storage->debug(0); | |
32 | ||
33 | ||
34 | done_testing; |
0 | use strict; | |
1 | use warnings; | |
2 | ||
3 | use Test::More; | |
4 | use Test::Exception; | |
5 | ||
6 | use lib qw(t/lib); | |
7 | use DBICTest; | |
8 | use DBIC::SqlMakerTest; | |
9 | ||
10 | my $schema = DBICTest->init_schema(); | |
11 | ||
12 | my $new_rs = $schema->resultset('Artist')->search({ | |
13 | 'artwork_to_artist.artist_id' => 1 | |
14 | }, { | |
15 | join => 'artwork_to_artist' | |
16 | }); | |
17 | lives_ok { $new_rs->count } 'regular search works'; | |
18 | lives_ok { $new_rs->search({ 'artwork_to_artist.artwork_cd_id' => 1})->count } | |
19 | '... and chaining off that using join works'; | |
20 | lives_ok { $new_rs->search({ 'artwork_to_artist.artwork_cd_id' => 1})->as_subselect_rs->count } | |
21 | '... and chaining off the virtual view works'; | |
22 | dies_ok { $new_rs->as_subselect_rs->search({'artwork_to_artist.artwork_cd_id'=> 1})->count } | |
23 | q{... but chaining off of a virtual view using join doesn't work}; | |
24 | done_testing; |