And another collapser issue
Peter Rabbitson [Mon, 11 Feb 2013 11:19:49 +0000 (12:19 +0100)]
One can not infer definedness from the presence of a parent *unless* we have
a 'belongs_to'-like relationship to that parent

lib/DBIx/Class/ResultSource/RowParser.pm
lib/DBIx/Class/Row.pm
t/resultset/rowparser_internals.t

index cc4706c..d4b75f2 100644 (file)
@@ -199,13 +199,14 @@ sub _resolve_collapse {
     }
   }
 
-  # if the parent is already defined, assume all of its related FKs are selected
+  # if the parent is already defined *AND* we have an inner reverse relationship
+  # (i.e. do not exist without it) , assume all of its related FKs are selected
   # (even if they in fact are NOT in the select list). Keep a record of what we
   # assumed, and if any such phantom-column becomes part of our own collapser,
   # throw everything assumed-from-parent away and replace with the collapser of
   # the parent (whatever it may be)
   my $assumed_from_parent;
-  unless ($args->{_parent_info}{underdefined}) {
+  if ( ! $args->{_parent_info}{underdefined} and ! $args->{_parent_info}{rev_rel_is_optional} ) {
     for my $col ( values %{$args->{_parent_info}{rel_condition} || {}} ) {
       next if exists $my_cols->{$col};
       $my_cols->{$col} = { via_collapse => $args->{_parent_info}{collapse_on_idcols} };
@@ -228,7 +229,6 @@ sub _resolve_collapse {
       if $args->{_parent_info}{collapser_reusable};
   }
 
-
   # Still dont know how to collapse - try to resolve based on our columns (plus already inserted FK bridges)
   if (
     ! $collapse_map->{-identifying_columns}
@@ -381,8 +381,10 @@ sub _resolve_collapse {
   # If we got that far - we are collapsable - GREAT! Now go down all children
   # a second time, and fill in the rest
 
-  $collapse_map->{-is_optional} = 1 if $args->{_parent_info}{is_optional};
-
+  $collapse_map->{-identifying_columns} = [ __unique_numlist(
+    @{ $args->{_parent_info}{collapse_on_idcols}||[] },
+    @{ $collapse_map->{-identifying_columns} },
+  )];
 
   my @id_sets;
   for my $rel (sort keys %$relinfo) {
@@ -396,7 +398,14 @@ sub _resolve_collapse {
 
         rel_condition => $relinfo->{$rel}{fk_map},
 
-        is_optional => $collapse_map->{-is_optional},
+        is_optional => ! $relinfo->{$rel}{is_inner},
+
+        # if there is at least one *inner* reverse relationship which is HASH-based (equality only)
+        # we can safely assume that the child can not exist without us
+        rev_rel_is_optional => ( first
+          { ref $_->{cond} eq 'HASH' and ($_->{attrs}{join_type}||'') !~ /^left/i }
+          values %{ $self->reverse_relationship_info($rel) },
+        ) ? 0 : 1,
 
         # if this is a 1:1 our own collapser can be used as a collapse-map
         # (regardless of left or not)
index a17c3f7..3a4dda1 100644 (file)
@@ -1187,7 +1187,7 @@ sub inflate_result {
     my @pre_vals;
     @pre_vals = (ref $prefetch->{$pre}[0] eq 'ARRAY')
       ? @{$prefetch->{$pre}} : $prefetch->{$pre}
-    if @{$prefetch->{$pre}};
+    if @{$prefetch->{$pre}||[]};
 
     my $pre_source = $source->related_source($pre);
 
index 9d71d4c..a89897b 100644 (file)
@@ -75,24 +75,24 @@ is_deeply (
     -identifying_columns => [ 4, 5 ],
 
     single_track => {
-      -identifying_columns => [ 4, 5 ],
+      -identifying_columns => [ 1, 4, 5 ],
       -is_optional => 1,
       -is_single => 1,
 
       cd => {
-        -identifying_columns => [ 4, 5 ],
+        -identifying_columns => [ 1, 4, 5 ],
         -is_single => 1,
 
         artist => {
-          -identifying_columns => [ 4, 5 ],
+          -identifying_columns => [ 1, 4, 5 ],
           -is_single => 1,
 
           cds => {
-            -identifying_columns => [ 3, 4, 5 ],
+            -identifying_columns => [ 1, 3, 4, 5 ],
             -is_optional => 1,
 
             tracks => {
-              -identifying_columns => [ 0, 3, 4, 5 ],
+              -identifying_columns => [ 0, 1, 3, 4, 5 ],
               -is_optional => 1,
             },
           },
@@ -117,7 +117,7 @@ is_same_src (
     ) {
 
       $cur_row_ids{$_} = defined $cur_row_data->[$_] ? $cur_row_data->[$_] : "\0NULL\xFF$rows_pos\xFF$_\0"
-        for (0, 3, 4, 5);
+        for (0, 1, 3, 4, 5);
 
       # a present cref in $_[1] implies lazy prefetch, implies a supplied stash in $_[2]
       $_[1] and $result_pos and unshift(@{$_[2]}, $cur_row_data) and last
@@ -127,21 +127,21 @@ is_same_src (
       $collapse_idx[0]{$cur_row_ids{4}}{$cur_row_ids{5}} ||= [{ artist => $cur_row_data->[5], title => $cur_row_data->[4], year => $cur_row_data->[2] }];
 
       # prefetch data of single_track (placed in root)
-      $collapse_idx[0]{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{single_track} ||= $collapse_idx[1]{$cur_row_ids{4}}{$cur_row_ids{5}};
+      $collapse_idx[0]{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{single_track} ||= $collapse_idx[1]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}};
 
       # prefetch data of cd (placed in single_track)
-      $collapse_idx[1]{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{cd} ||= $collapse_idx[2]{$cur_row_ids{4}}{$cur_row_ids{5}};
+      $collapse_idx[1]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{cd} ||= $collapse_idx[2]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}};
 
       # prefetch data of artist ( placed in single_track->cd)
-      $collapse_idx[2]{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{artist} ||= $collapse_idx[3]{$cur_row_ids{4}}{$cur_row_ids{5}} ||= [{ artistid => $cur_row_data->[1] }];
+      $collapse_idx[2]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{artist} ||= $collapse_idx[3]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}} ||= [{ artistid => $cur_row_data->[1] }];
 
       # prefetch data of cds (if available)
-      push @{$collapse_idx[3]{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{cds}}, $collapse_idx[4]{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}} ||= [{ cdid => $cur_row_data->[3] }]
-        unless $collapse_idx[4]{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}};
+      push @{$collapse_idx[3]{$cur_row_ids{1}}{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{cds}}, $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}} ||= [{ cdid => $cur_row_data->[3] }]
+        unless $collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}};
 
       # prefetch data of tracks (if available)
-      push @{$collapse_idx[4]{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{tracks}}, $collapse_idx[5]{$cur_row_ids{0}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}} ||= [{ title => $cur_row_data->[0] }]
-        unless $collapse_idx[5]{$cur_row_ids{0}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}};
+      push @{$collapse_idx[4]{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}}[1]{tracks}}, $collapse_idx[5]{$cur_row_ids{0}}{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}} ||= [{ title => $cur_row_data->[0] }]
+        unless $collapse_idx[5]{$cur_row_ids{0}}{$cur_row_ids{1}}{$cur_row_ids{3}}{$cur_row_ids{4}}{$cur_row_ids{5}};
 
       $_[0][$result_pos++] = $collapse_idx[0]{$cur_row_ids{4}}{$cur_row_ids{5}}
         if $is_new_res;