[Dbix-class] Iteration with ->next versus using ->all, and count
Adam Sjøgren
adsj at novozymes.com
Wed Sep 1 11:05:58 GMT 2010
Last week I upgraded to Ubuntu 10.04, and went from DBIx::Class 0.08107
to 0.08115.
In an application I noticed a change in iterating using ->next, compared
to calling ->all (on a resultset obtained with search_related), that I
find a little odd: One returns one record, the other returns three.
Here is an attempt at a minimal example (using Postgres and DBIx::Class
from git (93d0eb5)):
$ createdb cardb
$ PGOPTIONS='--client-min-messages=warning' psql cardb --quiet -f init.sql
$ DBIC_TRACE=1 ./test.pl
DBIx::Class::VERSION: 0.08115
SELECT me.id, me.company FROM fleet me WHERE ( me.id = ? ): '1'
SELECT COUNT( * ) FROM car me JOIN driver drivers ON drivers.car_id = me.id WHERE ( me.fleet_id = ? ): '1'
Count: 3
Iterator:
SELECT drivers.id, drivers.name, drivers.car_id FROM car me LEFT JOIN driver drivers ON drivers.car_id = me.id WHERE ( me.fleet_id = ? ): '1'
Christensen
All:
SELECT drivers.id, drivers.name, drivers.car_id FROM car me LEFT JOIN driver drivers ON drivers.car_id = me.id WHERE ( me.fleet_id = ? ): '1'
Christensen
Elgaard
Magnussen
$ cat test.pl
#!/usr/bin/perl
use warnings;
use strict;
use CarDB;
print 'DBIx::Class::VERSION: ' . $DBIx::Class::VERSION . "\n\n";
my $schema=CarDB->connect('dbi:Pg:dbname=cardb', 'core', 'core', { AutoCommit=>0 });
my $fleet=$schema->resultset('CarDB::Fleet')->find({ id=>1 });
my $drivers=$fleet->cars->search_related('drivers');
print "Count: " . $drivers->count . "\n";
print "\nIterator:\n";
while (my $driver=$drivers->next) {
print " " . $driver->name . "\n";
}
print "\nAll:\n";
foreach my $driver ($drivers->all) {
print " " . $driver->name . "\n";
}
$ cat init.sql
CREATE TABLE fleet (
id integer NOT NULL,
company text NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE car (
id integer NOT NULL,
plate text NOT NULL,
fleet_id integer REFERENCES fleet NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE driver (
id integer NOT NULL,
name text NOT NULL,
car_id integer REFERENCES car NOT NULL,
PRIMARY KEY (id)
);
INSERT INTO fleet (id, company) VALUES (1, 'Hertz');
INSERT INTO car (id, plate, fleet_id) VALUES (1, 'AAA 101', 1);
INSERT INTO car (id, plate, fleet_id) VALUES (2, 'BBB 202', 1);
INSERT INTO car (id, plate, fleet_id) VALUES (3, 'CCC 303', 1);
INSERT INTO car (id, plate, fleet_id) VALUES (4, 'DDD 404', 1);
INSERT INTO driver (id, name, car_id) VALUES (1, 'Christensen', 1);
INSERT INTO driver (id, name, car_id) VALUES (2, 'Elgaard', 3);
INSERT INTO driver (id, name, car_id) VALUES (3, 'Magnussen', 4);
$ cat CarDB.pm
package CarDB;
use strict;
use warnings;
use base qw(DBIx::Class::Schema);
__PACKAGE__->load_classes();
1;
$ cat CarDB/
Car.pm Driver.pm Fleet.pm
$ cat CarDB/Fleet.pm
package CarDB::Fleet;
use strict;
use warnings;
use base qw(DBIx::Class);
__PACKAGE__->load_components('PK::Auto', 'Core');
__PACKAGE__->table('fleet');
__PACKAGE__->add_columns(qw(id company));
__PACKAGE__->set_primary_key('id');
__PACKAGE__->has_many(cars=>'CarDB::Car', 'fleet_id', { cascade_delete=>0 });
1;
$ cat CarDB/Car.pm
package CarDB::Car;
use strict;
use warnings;
use base qw(DBIx::Class);
__PACKAGE__->load_components('PK::Auto', 'Core');
__PACKAGE__->table('car');
__PACKAGE__->add_columns(qw(id plate fleet_id));
__PACKAGE__->set_primary_key('id');
__PACKAGE__->belongs_to(fleet=>'CarDB::Fleet', 'fleet_id');
__PACKAGE__->has_many(drivers=>'CarDB::Driver', 'car_id', { cascade_delete=>0 });
1;
$ cat CarDB/Driver.pm
package CarDB::Driver;
use strict;
use warnings;
use base qw(DBIx::Class);
__PACKAGE__->load_components('PK::Auto', 'Core');
__PACKAGE__->table('driver');
__PACKAGE__->add_columns(qw(id name car_id));
__PACKAGE__->set_primary_key('id');
__PACKAGE__->belongs_to(car=>'CarDB::Car', 'car_id');
1;
$
Also, as seen above, if I call ->count on the resultset, the generated
SQL uses JOIN, while ->next and ->all both use LEFT JOIN - I was
expecting all three to run the "same" query?
Running the same example with DBIx::Class 0.08107 gives this result:
$ DBIC_TRACE=1 ./test.pl
DBIx::Class::VERSION: 0.08107
SELECT me.id, me.company FROM fleet me WHERE ( me.id = ? ): '1'
SELECT COUNT( * ) FROM car me LEFT JOIN driver drivers ON drivers.car_id = me.id WHERE ( me.fleet_id = ? ): '1'
Count: 4
Iterator:
SELECT drivers.id, drivers.name, drivers.car_id FROM car me LEFT JOIN driver drivers ON drivers.car_id = me.id WHERE ( me.fleet_id = ? ): '1'
Christensen
Use of uninitialized value in concatenation (.) or string at ./test.pl line 19.
Elgaard
Magnussen
All:
SELECT drivers.id, drivers.name, drivers.car_id FROM car me LEFT JOIN driver drivers ON drivers.car_id = me.id WHERE ( me.fleet_id = ? ): '1'
Christensen
Use of uninitialized value in concatenation (.) or string at ./test.pl line 24.
Elgaard
Magnussen
Issuing rollback() for database handle being DESTROY'd without explicit disconnect().
$
So in 0.08107 ->next and ->all do the same thing (albeit arguably the
empty driver shouldn't have been returned).
In 0.08115 ->count and ->all return the results I was expecting, but
->next doesn't.
Shouldn't it skip the record with no driver? (Or maybe both ->next and
->all should use JOIN, like ->count); I am not quite sure.
Maybe it is all driver error?
Best regards,
Adam
--
Adam Sjøgren
adsj at novozymes.com
More information about the DBIx-Class
mailing list