Fix two aliasing bugs: remove the alias when provided to new_result and add the alias...
Daniel Westermann-Clark [Mon, 7 Aug 2006 22:22:31 +0000 (22:22 +0000)]
Changes
lib/DBIx/Class/ResultSet.pm
t/64db.t
t/79aliasing.t [new file with mode: 0644]
t/lib/DBICTest.pm
t/lib/DBICTest/Schema.pm
t/lib/DBICTest/Schema/Agent.pm [new file with mode: 0644]
t/lib/DBICTest/Schema/Artist.pm
t/lib/DBICTest/Schema/Label.pm [new file with mode: 0644]
t/lib/sqlite.sql

diff --git a/Changes b/Changes
index 33e2775..5f687a3 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,6 +1,9 @@
 Revision history for DBIx::Class
 
 0.07001
+        - restore automatic aliasing in ResultSet::find() on nonunique queries
+        - allow aliases in ResultSet::find() queries (in cases of relationships
+          with prefetch)
         - pass $attrs to find from update_or_create so a specific key can be
           provided
         - remove anonymous blesses to avoid major speed hit on Fedora Core 5's
index 13d0d76..b6d85b6 100644 (file)
@@ -323,9 +323,13 @@ sub find {
 
   my @unique_queries = $self->_unique_queries($input_query, $attrs);
 
-  # Handle cases where the ResultSet defines the query, or where the user is
-  # abusing find
-  my $query = @unique_queries ? \@unique_queries : $input_query;
+  # Build the final query: Default to the disjunction of the unique queries,
+  # but allow the input query in case the ResultSet defines the query or the
+  # user is abusing find
+  my $alias = exists $attrs->{alias} ? $attrs->{alias} : $self->{attrs}{alias};
+  my $query = @unique_queries
+    ? [ map { $self->_add_alias($_, $alias) } @unique_queries ]
+    : $self->_add_alias($input_query, $alias);
 
   # Run the query
   if (keys %$attrs) {
@@ -339,6 +343,22 @@ sub find {
   }
 }
 
+# _add_alias
+#
+# Add the specified alias to the specified query hash. A copy is made so the
+# original query is not modified.
+
+sub _add_alias {
+  my ($self, $query, $alias) = @_;
+
+  my %aliased = %$query;
+  foreach my $col (grep { ! m/\./ } keys %aliased) {
+    $aliased{"$alias.$col"} = delete $aliased{$col};
+  }
+
+  return \%aliased;
+}
+
 # _unique_queries
 #
 # Build a list of queries which satisfy unique constraints.
@@ -346,7 +366,6 @@ sub find {
 sub _unique_queries {
   my ($self, $query, $attrs) = @_;
 
-  my $alias = $self->{attrs}{alias};
   my @constraint_names = exists $attrs->{key}
     ? ($attrs->{key})
     : $self->result_source->unique_constraint_names;
@@ -359,11 +378,6 @@ sub _unique_queries {
     my $num_query = scalar keys %$unique_query;
     next unless $num_query;
 
-    # Add the ResultSet's alias
-    foreach my $col (grep { ! m/\./ } keys %$unique_query) {
-      $unique_query->{"$alias.$col"} = delete $unique_query->{$col};
-    }
-
     # XXX: Assuming quite a bit about $self->{attrs}{where}
     my $num_cols = scalar @unique_cols;
     my $num_where = exists $self->{attrs}{where}
@@ -1194,16 +1208,35 @@ sub new_result {
   $self->throw_exception(
     "Can't abstract implicit construct, condition not a hash"
   ) if ($self->{cond} && !(ref $self->{cond} eq 'HASH'));
-  my %new = %$values;
+
   my $alias = $self->{attrs}{alias};
-  foreach my $key (keys %{$self->{cond}||{}}) {
-    $new{$1} = $self->{cond}{$key} if ($key =~ m/^(?:\Q${alias}.\E)?([^.]+)$/);
-  }
+  my %new = (
+    %{ $self->_remove_alias($values, $alias) },
+    %{ $self->_remove_alias($self->{cond}, $alias) },
+  );
+
   my $obj = $self->result_class->new(\%new);
   $obj->result_source($self->result_source) if $obj->can('result_source');
   return $obj;
 }
 
+# _remove_alias
+#
+# Remove the specified alias from the specified query hash. A copy is made so
+# the original query is not modified.
+
+sub _remove_alias {
+  my ($self, $query, $alias) = @_;
+
+  my %unaliased = %{ $query || {} };
+  foreach my $key (keys %unaliased) {
+    $unaliased{$1} = delete $unaliased{$key}
+      if $key =~ m/^(?:\Q$alias\E\.)?([^.]+)$/;
+  }
+
+  return \%unaliased;
+}
+
 =head2 find_or_new
 
 =over 4
index d9c03aa..47aff46 100644 (file)
--- a/t/64db.t
+++ b/t/64db.t
@@ -41,6 +41,7 @@ my $type_info = $schema->storage->columns_info_for('artist');
 
 delete $type_info->{artistid}{size};
 delete $type_info->{name}{size};
+delete $type_info->{agent}{size};
 
 my $test_type_info = {
     'artistid' => {
@@ -51,6 +52,10 @@ my $test_type_info = {
         'data_type' => 'varchar',
         'is_nullable' => 0,
     },
+    'agent' => {
+        'data_type' => 'integer',
+        'is_nullable' => 0,
+    },
 };
 is_deeply($type_info, $test_type_info, 'columns_info_for - column data types');
 
diff --git a/t/79aliasing.t b/t/79aliasing.t
new file mode 100644 (file)
index 0000000..e0eb04c
--- /dev/null
@@ -0,0 +1,42 @@
+use strict;
+use warnings;  
+
+use Test::More;
+use lib qw(t/lib);
+use DBICTest;
+
+my $schema = DBICTest->init_schema();
+
+plan tests => 8;
+
+my $label = $schema->resultset('Label')->find({ labelid => 1 });
+
+# Check that you can leave off the alias
+{
+  my $existing_agent = $label->agents->find_or_create({
+    name => 'Ted',
+  });
+  ok(! $existing_agent->is_changed, 'find_or_create on prefetched has_many with same column names: row is clean');
+  is($existing_agent->name, 'Ted', 'find_or_create on prefetched has_many with same column names: name matches existing entry');
+
+  my $new_agent = $label->agents->find_or_create({
+    name => 'Someone Else',
+  });
+  ok(! $new_agent->is_changed, 'find_or_create on prefetched has_many with same column names: row is clean');
+  is($new_agent->name, 'Someone Else', 'find_or_create on prefetched has_many with same column names: name matches');
+}
+
+# Check that you can specify the alias
+{
+  my $existing_agent = $label->agents->find_or_create({
+    'me.name' => 'Someone Else',
+  });
+  ok(! $existing_agent->is_changed, 'find_or_create on prefetched has_many with same column names: row is clean');
+  is($existing_agent->name, 'Someone Else', 'find_or_create on prefetched has_many with same column names: can be disambiguated with "me." for existing entry');
+
+  my $new_agent = $label->agents->find_or_create({
+    'me.name' => 'Some New Guy',
+  });
+  ok(! $new_agent->is_changed, 'find_or_create on prefetched has_many with same column names: row is clean');
+  is($new_agent->name, 'Some New Guy', 'find_or_create on prefetched has_many with same column names: can be disambiguated with "me." for new entry');
+}
index a050862..e691999 100755 (executable)
@@ -104,11 +104,21 @@ sub populate_schema {
     my $self = shift;
     my $schema = shift;
 
+    $schema->populate('Label', [
+        [ qw/labelid name/ ],
+        [ 1, 'Acme Records' ],
+    ]);
+
+    $schema->populate('Agent', [
+        [ qw/agentid label name/ ],
+        [ 1, 1, 'Ted' ],
+    ]);
+
     $schema->populate('Artist', [
-        [ qw/artistid name/ ],
-        [ 1, 'Caterwauler McCrae' ],
-        [ 2, 'Random Boy Band' ],
-        [ 3, 'We Are Goth' ],
+        [ qw/artistid agent name/ ],
+        [ 1, 1, 'Caterwauler McCrae' ],
+        [ 2, 1, 'Random Boy Band' ],
+        [ 3, 1, 'We Are Goth' ],
     ]);
 
     $schema->populate('CD', [
index 8e7597d..f8b9816 100644 (file)
@@ -6,9 +6,11 @@ use base qw/DBIx::Class::Schema/;
 no warnings qw/qw/;
 
 __PACKAGE__->load_classes(qw/
+  Agent
   Artist
   Employee
   CD
+  Label
   Link
   Bookmark
   #dummy
diff --git a/t/lib/DBICTest/Schema/Agent.pm b/t/lib/DBICTest/Schema/Agent.pm
new file mode 100644 (file)
index 0000000..6434393
--- /dev/null
@@ -0,0 +1,26 @@
+package # hide from PAUSE 
+    DBICTest::Schema::Agent;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->table('agent');
+__PACKAGE__->add_columns(
+  'agentid' => {
+    data_type         => 'integer',
+    is_auto_increment => 1
+  },
+  'label' => {
+    data_type => 'integer',
+  },
+  'name' => {
+    data_type   => 'varchar',
+    size        => 100,
+    is_nullable => 1,
+  },
+);
+__PACKAGE__->set_primary_key('agentid');
+
+__PACKAGE__->has_many( artists => 'DBICTest::Schema::Artist' );
+__PACKAGE__->belongs_to( label => 'DBICTest::Schema::Label' );
+
+1;
index 0bb49c4..66d15b5 100644 (file)
@@ -9,6 +9,10 @@ __PACKAGE__->add_columns(
     data_type => 'integer',
     is_auto_increment => 1
   },
+  'agent' => {
+    data_type   => 'integer',
+    is_nullable => 1,
+  },
   'name' => {
     data_type => 'varchar',
     size      => 100,
@@ -19,6 +23,7 @@ __PACKAGE__->set_primary_key('artistid');
 
 __PACKAGE__->mk_classdata('field_name_for', {
     artistid    => 'primary key',
+    agent       => 'agent',
     name        => 'artist name',
 });
 
@@ -27,6 +32,8 @@ __PACKAGE__->has_many(
     { order_by => 'year' },
 );
 
+__PACKAGE__->belongs_to( agent => 'DBICTest::Schema::Agent' );
+
 __PACKAGE__->has_many( twokeys => 'DBICTest::Schema::TwoKeys' );
 __PACKAGE__->has_many( onekeys => 'DBICTest::Schema::OneKey' );
 
diff --git a/t/lib/DBICTest/Schema/Label.pm b/t/lib/DBICTest/Schema/Label.pm
new file mode 100644 (file)
index 0000000..2d79bf4
--- /dev/null
@@ -0,0 +1,26 @@
+package # hide from PAUSE 
+    DBICTest::Schema::Label;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->table('label');
+__PACKAGE__->add_columns(
+  'labelid' => {
+    data_type         => 'integer',
+    is_auto_increment => 1
+  },
+  'name' => {
+    data_type   => 'varchar',
+    size        => 100,
+    is_nullable => 1,
+  },
+);
+__PACKAGE__->set_primary_key('labelid');
+
+__PACKAGE__->has_many(
+  agents => 'DBICTest::Schema::Agent',
+  undef,
+  { prefetch => 'artists' }
+);
+
+1;
index fd50c36..5846973 100644 (file)
@@ -1,6 +1,6 @@
 -- 
 -- Created by SQL::Translator::Producer::SQLite
--- Created on Sun Jul 23 00:23:30 2006
+-- Created on Fri Aug  4 19:03:21 2006
 -- 
 BEGIN TRANSACTION;
 
@@ -44,6 +44,15 @@ CREATE TABLE cd_to_producer (
 --
 CREATE TABLE artist (
   artistid INTEGER PRIMARY KEY NOT NULL,
+  agent integer,
+  name varchar(100)
+);
+
+--
+-- Table: label
+--
+CREATE TABLE label (
+  labelid INTEGER PRIMARY KEY NOT NULL,
   name varchar(100)
 );
 
@@ -119,15 +128,6 @@ CREATE TABLE self_ref (
 );
 
 --
--- Table: link
---
-CREATE TABLE link (
-  id INTEGER PRIMARY KEY NOT NULL,
-  url varchar(100),
-  title varchar(100)
-);
-
---
 -- Table: tags
 --
 CREATE TABLE tags (
@@ -137,6 +137,24 @@ CREATE TABLE tags (
 );
 
 --
+-- Table: agent
+--
+CREATE TABLE agent (
+  agentid INTEGER PRIMARY KEY NOT NULL,
+  label integer NOT NULL,
+  name varchar(100)
+);
+
+--
+-- Table: link
+--
+CREATE TABLE link (
+  id INTEGER PRIMARY KEY NOT NULL,
+  url varchar(100),
+  title varchar(100)
+);
+
+--
 -- Table: treelike
 --
 CREATE TABLE treelike (