On Jan 16, 2008 10:54 AM, Ash Berlin <[EMAIL PROTECTED]> wrote:
>
>
> On Jan 16, 2008, at 9:02 AM, Zbigniew Lukasiak wrote:
>
> > Hi all
> >
> > This is a continuation of the "patch for ResultSet::find_or_new"
> > thread - but it's also a subject on it's own.
> >
> > It is my understanding that:
> >
> > $schema->resultset("Artist")->find({name => 'Random Girl Band'}, {key
> > => 'primary'});
> >
> > should never find anything - because it should search by the primary
> > key that is not included in the search conditions. And I believe I am
> > not alone in this interpretation of the documentation - see for
> > example Matt's words:
> >
> >> The usual use of find_or_new is to pass a unique key plus additional
> >> attributes to be used for object creation (which are ignored in the
> >> find()
> >> by specifying the key attr as well).
> >
> > But if there is a row with name == 'Random Girl Band' then it will
> > find it. To prove that I attach a patch for the t/61findnot.t test.
> >
> > --
> > Zbigniew Lukasiak
> > http://brudnopis.blogspot.com/
> > <61findnot.diff>_______________________________________________
> >
>
> Seems sensible to me - the patch shows the behaviour I would *expect*.
OK. Patch is attached - perhaps someone would instruct me what would
be the right way to specify the 'FALSE' condition for the query.
But after having done that patch I think that this is actually a very
corner case - and perhaps it is a bug to not include the unique keys
in the query when you do find and we should not do that.
I've started this investigation because of update_or_create (and
find_or_create and find_or_new - all of which use the same logic).
They are useful in the case when you receive back some fields from a
web form and you want to use the same code for updating an existing
record and creating a new one. But when you do find_or_create( {
primary_key => undef, other_col => 'some value', ... } ) then it will
fail in PostgreSQL and the answer was to do find_or_create( {other_col
=> 'some value', ... } ) and, as the test case shows, this was failing
to do the right thing either so I was stuck.
An alternative could be to insert DEFAULT by the PostgreSQL storage if
the primary key inserted is NULL (MySQL and SQLite would work fine
with inserting a NULL pk).
I don't know - all I wanted was a working update_or_create method.
--
Zbigniew Lukasiak
http://perlalchemy.blogspot.com/
Index: t/69update.t
===================================================================
--- t/69update.t (revision 3934)
+++ t/69update.t (working copy)
@@ -9,7 +9,7 @@
BEGIN {
eval "use DBD::SQLite";
- plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 6);
+ plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 9);
}
my $art = $schema->resultset("Artist")->find(1);
@@ -34,3 +34,32 @@
my $art_100 = $schema->resultset("Artist")->find(100);
$art_100->artistid(101);
ok($art_100->update(), 'update allows pk mutation via column accessor');
+
+$art_100->cds->delete;
+$art_100->update( {
+ cds => [
+ { title => 'Picnic at Hanging Rock', year => '1975' },
+ { title => 'Blade Runner', year => '1982' },
+ { title => 'The Deer Hunter', year => '1978' }
+ ]
+ }
+);
+my @cds = $art_100->cds;
+my $picnick_id = $art_100->cds->search( { title => 'Picnic at Hanging Rock' } )->first->cdid;
+my $bladerunner_id = $art_100->cds->search( { title => 'Blade Runner' } )->first->cdid;
+is ( scalar @cds, 3, 'CDs create in an update' );
+
+$art_100->update( {
+ cds => [
+ { cdid => $picnick_id, title => 'Picnic at Hanging Rock', year => '1975' },
+ { cdid => $bladerunner_id, title => 'Blade Runner', year => '2007' }
+ ]
+ }
+);
+# What to do with the 'The Deer Hunter' - which should now be unrelated?
+# Should it be deleted or just the foreign key should be set to NULL?
[EMAIL PROTECTED] = $art_100->cds;
+is ( scalar @cds, 2, 'CDs number correct after artist update' );
+my $bladerunner = $schema->resultset("CD")->find($bladerunner_id);
+is ( $bladerunner->year, '2007', 'CD updated in an artist update' );
+
Index: t/61findnot.t
===================================================================
--- t/61findnot.t (revision 3934)
+++ t/61findnot.t (working copy)
@@ -7,7 +7,7 @@
my $schema = DBICTest->init_schema();
-plan tests => 17;
+plan tests => 18;
my $art = $schema->resultset("Artist")->find(4);
ok(!defined($art), 'Find on primary id: artist not found');
@@ -44,3 +44,9 @@
@cd = $cd->single;
cmp_ok(@cd, '==', 1, 'Return something even in array context');
ok(@cd && !defined($cd[0]), 'Array contains an undef as only element');
+
+$schema->resultset("Artist")->create({name => 'Random Girl Band'});
+$art = $schema->resultset("Artist")->find({name => 'Random Girl Band'}, {dont_fallback => 1});
+ok(!defined($art), 'Find on primary key with no key provided: artist not found');
+
+
Index: lib/DBIx/Class/ResultSet.pm
===================================================================
--- lib/DBIx/Class/ResultSet.pm (revision 3934)
+++ lib/DBIx/Class/ResultSet.pm (working copy)
@@ -321,6 +321,11 @@
If no C<key> is specified, it searches on all unique constraints defined on the
source, including the primary key.
+If you specify columns explicitely and there are no columns with unique constraints
+among them then C<find> currently will fall back to search by all other columns.
+Don't depend on that functionality - as it can change in the future. To not fallback
+set the 'dont_fallback' attribute to 1;
+
If your table does not have a primary key, you B<must> provide a value for the
C<key> attribute matching one of the unique constraints on the source.
@@ -381,10 +386,19 @@
# 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);
-
+ my $query;
+ if ( ! @unique_queries ){
+ if ( delete $attrs->{dont_fallback} ) {
+ $query = { 0 => 1 };
+ }
+ else{
+ carp "No unique fields found in query. Falling back to search by all query fields. This usage is deprecated. For not falling back use 'dont_fallback' attribute";
+ $query = $self->_add_alias($input_query, $alias);
+ }
+ }
+ else{
+ $query = [ map { $self->_add_alias($_, $alias) } @unique_queries ];
+ }
# Run the query
if (keys %$attrs) {
my $rs = $self->search($query, $attrs);
_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/[EMAIL PROTECTED]