[Dbix-class] RFC: What to do with $resultset->single returning
multiple rows?
John Napiorkowski
jjn1056 at yahoo.com
Tue May 6 01:19:01 BST 2008
Hi,
While trying to track down a bug in .08 trunk that was causing an error on mysql I discovered that the problem was due to a change in the way DBIx::Class::Storage::DBI->select_single handled results that returned more than a single row. In 0.08010 and earlier, if ->select_single was run against SQL that returned more than a single row, only the first row was returned and the remaining ones silently discarded. On DBIC trunk, a bit of code was added that would carp out if more rows existed:
sub select_single {
my $self = shift;
my ($rv, $sth, @bind) = $self->_select(@_);
my @row = $sth->fetchrow_array;
carp "Query returned more than one row" if $sth->fetchrow_array;
# Need to call finish() to work round broken DBDs
$sth->finish();
return @row;
}
Now, from what I can see, the entire point of this method is to help optimise $resultset->find by just returning a single row without creating a whole cursor. However, DBIC::Storage::DBI->select_single is also publicly exposed as $resultset->single and is documented as a optimised select for a single row. Nothing in the docs or previous versions warned users that >single should only be run against SQL that returns a single row. In fact, one would think that ->find is reserved for this purpose. So by adding this additional constraint at that point I am very certain we are going to be breaking peoples code. I know in fact $resultset->single is used in a few spots in our codebase in places that will explode if the above carp is kept.
I was lead to this because the mysql error was due to the fact the line:
carp "Query returned more than one row" if $sth->fetchrow_array;
is going to die when the $sth is exhausted (which was generating 'fetch without execute' errors). I could fix this to work properly, but I really feel it's not such a good idea to change the API behavior like this without some sort of deprecation cycle. In addition I am not certain why should change it. I can see a need for a quick, "get me the first something you find" function. However at the very least we should update the docs to reflect clarified semantices between $resultset->first, ->single, and ->find.
So what do you all think? Our options are:
-1- Revert ->select_single to the old behavior but deprecate $resultset->single so that we can eventually remove it from the API at some point, freeing us to constraint ->select_single when it's no longer part of the public API
-2- Revert ->select_single to the old behavior and deprecate $resultset->single for SQL that returns multiple rows. Change the carp to some sort of warn and fix the 'fetch without execute' bug.
-3- Revert ->select_single to the old behavior and properly document the existing behavior, in particular to clarify the differences between ->first, ->single, and ->find. This is basically reverting to the 0.08010 behavior, documenting it, adding a test, etc.
-4-> Push this change through. Fix the 'fetch without excute bug' and tell everyone they need to grep/ack through their code and replace ->single with either ->first or ->find for the cases the underlying SQL could return more than one row.
To be honest I prefer option 3, since that's the easiest thing for me and my deployment. I really don't see much of a use for $resultset->single that only is useful against SQL that is guaranteed to return a single row. Seems to me that is what ->find is for. However I do see value in a cursorless form of ->first, since sometimes you just want to grab the first thing as fast as possible.
Certainly at the minimum we should clarify the docs. I think we should push people away from ->single if what they really want in ->first.
I'm ready to make these changes, fix up the docs, add a test. Let's just decide so it's part of this next release.
--John
____________________________________________________________________________________
Be a better friend, newshound, and
know-it-all with Yahoo! Mobile. Try it now. http://mobile.yahoo.com/;_ylt=Ahu06i62sR8HDtDypao8Wcj9tAcJ
More information about the DBIx-Class
mailing list