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]

Reply via email to