Merge 'trunk' into 'chaining_fixes'
Peter Rabbitson [Thu, 11 Feb 2010 11:21:52 +0000 (11:21 +0000)]
r8580@Thesaurus (orig r8567):  gshank | 2010-02-05 22:29:24 +0100
add doc on 'where' attribute

r8587@Thesaurus (orig r8574):  frew | 2010-02-07 21:07:03 +0100
add as_subselect_rs
r8588@Thesaurus (orig r8575):  frew | 2010-02-07 21:13:04 +0100
fix longstanding unmentioned bug ("me")
r8589@Thesaurus (orig r8576):  frew | 2010-02-08 06:17:43 +0100
another example of as_subselect_rs
r8590@Thesaurus (orig r8577):  frew | 2010-02-08 06:23:58 +0100
fix bug in UTF8Columns
r8591@Thesaurus (orig r8578):  ribasushi | 2010-02-08 09:31:01 +0100
Extend utf8columns test to trap fixed bug
r8592@Thesaurus (orig r8579):  ribasushi | 2010-02-08 12:03:23 +0100
Cleanup rel accessor type handling
r8593@Thesaurus (orig r8580):  ribasushi | 2010-02-08 12:20:47 +0100
Fix some fallout
r8595@Thesaurus (orig r8582):  ribasushi | 2010-02-08 12:38:19 +0100
Merge some obsolete code cleanup from the prefetch branch
r8596@Thesaurus (orig r8583):  ribasushi | 2010-02-08 12:42:09 +0100
Merge fix of RT54039 from prefetch branch
r8598@Thesaurus (orig r8585):  ribasushi | 2010-02-08 12:48:31 +0100
Release 0.08118
r8600@Thesaurus (orig r8587):  ribasushi | 2010-02-08 12:52:33 +0100
Bump trunk version
r8606@Thesaurus (orig r8593):  ribasushi | 2010-02-08 16:16:44 +0100
cheaper lookup
r8609@Thesaurus (orig r8596):  ribasushi | 2010-02-10 12:40:37 +0100
Consolidate last_insert_id handling with a fallback-attempt on DBI::last_insert_id
r8614@Thesaurus (orig r8601):  caelum | 2010-02-10 21:29:51 +0100
workaround for Moose bug affecting Replicated storage
r8615@Thesaurus (orig r8602):  caelum | 2010-02-10 21:40:07 +0100
revert Moose bug workaround, bump Moose dep for Replicated to 0.98
r8616@Thesaurus (orig r8603):  caelum | 2010-02-10 22:48:34 +0100
add a couple proxy methods to Replicated so it can run
r8628@Thesaurus (orig r8615):  caelum | 2010-02-11 11:35:01 +0100
 r21090@hlagh (orig r7836):  caelum | 2009-11-02 06:40:52 -0500
 new branch to fix unhandled methods in Storage::DBI::Replicated
 r21091@hlagh (orig r7837):  caelum | 2009-11-02 06:42:00 -0500
 add test to display unhandled methods
 r21092@hlagh (orig r7838):  caelum | 2009-11-02 06:55:34 -0500
 minor fix to last committed test
 r21093@hlagh (orig r7839):  caelum | 2009-11-02 09:26:00 -0500
 minor test code cleanup
 r23125@hlagh (orig r8607):  caelum | 2010-02-10 19:25:51 -0500
 add unimplemented Storage::DBI methods to ::DBI::Replicated
 r23130@hlagh (orig r8612):  ribasushi | 2010-02-11 05:12:48 -0500
 Podtesting exclusion

r8630@Thesaurus (orig r8617):  frew | 2010-02-11 11:45:54 +0100
Changes (from a while ago)
r8631@Thesaurus (orig r8618):  caelum | 2010-02-11 11:46:58 +0100
savepoints for SQLAnywhere

1  2 
lib/DBIx/Class/ResultSet.pm

@@@ -291,14 -291,10 +291,14 @@@ sub search_rs 
      $rows = $self->get_cache;
    }
  
 +  if (List::Util::first { exists $attrs->{$_} } qw{columns select as}) {
 +     delete @{$our_attrs}{qw{select as columns +select +as +columns}};
 +  }
 +
    my $new_attrs = { %{$our_attrs}, %{$attrs} };
  
    # merge new attrs into inherited
 -  foreach my $key (qw/join prefetch +select +as bind/) {
 +  foreach my $key (qw/join prefetch +select +as +columns bind/) {
      next unless exists $attrs->{$key};
      $new_attrs->{$key} = $self->_merge_attr($our_attrs->{$key}, $attrs->{$key});
    }
@@@ -2592,6 -2588,68 +2592,68 @@@ sub current_source_alias 
    return ($self->{attrs} || {})->{alias} || 'me';
  }
  
+ =head2 as_subselect_rs
+ =over 4
+ =item Arguments: none
+ =item Return Value: $resultset
+ =back
+ Act as a barrier to SQL symbols.  The resultset provided will be made into a
+ "virtual view" by including it as a subquery within the from clause.  From this
+ point on, any joined tables are inaccessible to ->search on the resultset (as if
+ it were simply where-filtered without joins).  For example:
+  my $rs = $schema->resultset('Bar')->search({'x.name' => 'abc'},{ join => 'x' });
+  # 'x' now pollutes the query namespace
+  # So the following works as expected
+  my $ok_rs = $rs->search({'x.other' => 1});
+  # But this doesn't: instead of finding a 'Bar' related to two x rows (abc and
+  # def) we look for one row with contradictory terms and join in another table
+  # (aliased 'x_2') which we never use
+  my $broken_rs = $rs->search({'x.name' => 'def'});
+  my $rs2 = $rs->as_subselect_rs;
+  # doesn't work - 'x' is no longer accessible in $rs2, having been sealed away
+  my $not_joined_rs = $rs2->search({'x.other' => 1});
+  # works as expected: finds a 'table' row related to two x rows (abc and def)
+  my $correctly_joined_rs = $rs2->search({'x.name' => 'def'});
+ Another example of when one might use this would be to select a subset of
+ columns in a group by clause:
+  my $rs = $schema->resultset('Bar')->search(undef, {
+    group_by => [qw{ id foo_id baz_id }],
+  })->as_subselect_rs->search(undef, {
+    columns => [qw{ id foo_id }]
+  });
+ In the above example normally columns would have to be equal to the group by,
+ but because we isolated the group by into a subselect the above works.
+ =cut
+ sub as_subselect_rs {
+    my $self = shift;
+    return $self->result_source->resultset->search( undef, {
+       alias => $self->current_source_alias,
+       from => [{
+             $self->current_source_alias => $self->as_query,
+             -alias         => $self->current_source_alias,
+             -source_handle => $self->result_source->handle,
+          }]
+    });
+ }
  # This code is called by search_related, and makes sure there
  # is clear separation between the joins before, during, and
  # after the relationship. This information is needed later
@@@ -2719,46 -2777,41 +2781,46 @@@ sub _resolved_attrs 
    # build columns (as long as select isn't set) into a set of as/select hashes
    unless ( $attrs->{select} ) {
  
 -    my @cols = ( ref($attrs->{columns}) eq 'ARRAY' )
 -      ? @{ delete $attrs->{columns}}
 -      : (
 -          ( delete $attrs->{columns} )
 -            ||
 -          $source->columns
 -        )
 -    ;
 -
 -    @colbits = map {
 -      ( ref($_) eq 'HASH' )
 -      ? $_
 -      : {
 -          (
 -            /^\Q${alias}.\E(.+)$/
 -              ? "$1"
 -              : "$_"
 -          )
 -            =>
 -          (
 -            /\./
 -              ? "$_"
 -              : "${alias}.$_"
 -          )
 -        }
 -    } @cols;
 +    my @cols;
 +    if ( ref $attrs->{columns} eq 'ARRAY' ) {
 +      @cols = @{ delete $attrs->{columns}}
 +    } elsif ( defined $attrs->{columns} ) {
 +      @cols = delete $attrs->{columns}
 +    } else {
 +      @cols = $source->columns
 +    }
 +
 +    for (@cols) {
 +      if ( ref $_ eq 'HASH' ) {
 +        push @colbits, $_
 +      } else {
 +        my $key = /^\Q${alias}.\E(.+)$/
 +          ? "$1"
 +          : "$_";
 +        my $value = /\./
 +          ? "$_"
 +          : "${alias}.$_";
 +        push @colbits, { $key => $value };
 +      }
 +    }
    }
  
    # add the additional columns on
 -  foreach ( 'include_columns', '+columns' ) {
 -      push @colbits, map {
 -          ( ref($_) eq 'HASH' )
 -            ? $_
 -            : { ( split( /\./, $_ ) )[-1] => ( /\./ ? $_ : "${alias}.$_" ) }
 -      } ( ref($attrs->{$_}) eq 'ARRAY' ) ? @{ delete $attrs->{$_} } : delete $attrs->{$_} if ( $attrs->{$_} );
 +  foreach (qw{include_columns +columns}) {
 +    if ( $attrs->{$_} ) {
 +      my @list = ( ref($attrs->{$_}) eq 'ARRAY' )
 +        ? @{ delete $attrs->{$_} }
 +        : delete $attrs->{$_};
 +      for (@list) {
 +        if ( ref($_) eq 'HASH' ) {
 +          push @colbits, $_
 +        } else {
 +          my $key = ( split /\./, $_ )[-1];
 +          my $value = ( /\./ ? $_ : "$alias.$_" );
 +          push @colbits, { $key => $value };
 +        }
 +      }
 +    }
    }
  
    # start with initial select items
          ( ref $attrs->{select} eq 'ARRAY' )
        ? [ @{ $attrs->{select} } ]
        : [ $attrs->{select} ];
 -    $attrs->{as} = (
 -      $attrs->{as}
 -      ? (
 -        ref $attrs->{as} eq 'ARRAY'
 -        ? [ @{ $attrs->{as} } ]
 -        : [ $attrs->{as} ]
 +
 +    if ( $attrs->{as} ) {
 +      $attrs->{as} =
 +        (
 +          ref $attrs->{as} eq 'ARRAY'
 +            ? [ @{ $attrs->{as} } ]
 +            : [ $attrs->{as} ]
          )
 -      : [ map { m/^\Q${alias}.\E(.+)$/ ? $1 : $_ } @{ $attrs->{select} } ]
 -    );
 +    } else {
 +      $attrs->{as} = [ map {
 +         m/^\Q${alias}.\E(.+)$/
 +           ? $1
 +           : $_
 +         } @{ $attrs->{select} }
 +      ]
 +    }
    }
    else {
  
    }
  
    # now add colbits to select/as
 -  push( @{ $attrs->{select} }, map { values( %{$_} ) } @colbits );
 -  push( @{ $attrs->{as} },     map { keys( %{$_} ) } @colbits );
 +  push @{ $attrs->{select} }, map values %{$_}, @colbits;
 +  push @{ $attrs->{as}     }, map keys   %{$_}, @colbits;
  
 -  my $adds;
 -  if ( $adds = delete $attrs->{'+select'} ) {
 +  if ( my $adds = delete $attrs->{'+select'} ) {
      $adds = [$adds] unless ref $adds eq 'ARRAY';
 -    push(
 -      @{ $attrs->{select} },
 -      map { /\./ || ref $_ ? $_ : "${alias}.$_" } @$adds
 -    );
 +    push @{ $attrs->{select} },
 +      map { /\./ || ref $_ ? $_ : "$alias.$_" } @$adds;
    }
 -  if ( $adds = delete $attrs->{'+as'} ) {
 +  if ( my $adds = delete $attrs->{'+as'} ) {
      $adds = [$adds] unless ref $adds eq 'ARRAY';
 -    push( @{ $attrs->{as} }, @$adds );
 +    push @{ $attrs->{as} }, @$adds;
    }
  
 -  $attrs->{from} ||= [ {
 +  $attrs->{from} ||= [{
      -source_handle => $source->handle,
      -alias => $self->{attrs}{alias},
      $self->{attrs}{alias} => $source->from,
 -  } ];
 +  }];
  
    if ( $attrs->{join} || $attrs->{prefetch} ) {
  
            $join,
            $alias,
            { %{ $attrs->{seen_join} || {} } },
 -          ($attrs->{seen_join} && keys %{$attrs->{seen_join}})
 +          ( $attrs->{seen_join} && keys %{$attrs->{seen_join}})
              ? $attrs->{from}[-1][0]{-join_path}
              : []
            ,