From: Daniel Westermann-Clark Date: Mon, 7 Aug 2006 22:22:31 +0000 (+0000) Subject: Fix two aliasing bugs: remove the alias when provided to new_result and add the alias... X-Git-Tag: v0.07002~51 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=dbsrgits%2FDBIx-Class.git;a=commitdiff_plain;h=ab8481f509e0c74b2dbe61441564c250b457193e;hp=824f4422ff33f9e1052507e613f7b9086f5c3103 Fix two aliasing bugs: remove the alias when provided to new_result and add the alias to the input query if we're using it (regression from 0.06) --- diff --git a/Changes b/Changes index 33e2775..5f687a3 100644 --- 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 diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index 13d0d76..b6d85b6 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -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 diff --git a/t/64db.t b/t/64db.t index d9c03aa..47aff46 100644 --- 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 index 0000000..e0eb04c --- /dev/null +++ b/t/79aliasing.t @@ -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'); +} diff --git a/t/lib/DBICTest.pm b/t/lib/DBICTest.pm index a050862..e691999 100755 --- a/t/lib/DBICTest.pm +++ b/t/lib/DBICTest.pm @@ -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', [ diff --git a/t/lib/DBICTest/Schema.pm b/t/lib/DBICTest/Schema.pm index 8e7597d..f8b9816 100644 --- a/t/lib/DBICTest/Schema.pm +++ b/t/lib/DBICTest/Schema.pm @@ -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 index 0000000..6434393 --- /dev/null +++ b/t/lib/DBICTest/Schema/Agent.pm @@ -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; diff --git a/t/lib/DBICTest/Schema/Artist.pm b/t/lib/DBICTest/Schema/Artist.pm index 0bb49c4..66d15b5 100644 --- a/t/lib/DBICTest/Schema/Artist.pm +++ b/t/lib/DBICTest/Schema/Artist.pm @@ -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 index 0000000..2d79bf4 --- /dev/null +++ b/t/lib/DBICTest/Schema/Label.pm @@ -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; diff --git a/t/lib/sqlite.sql b/t/lib/sqlite.sql index fd50c36..5846973 100644 --- a/t/lib/sqlite.sql +++ b/t/lib/sqlite.sql @@ -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 (