gshank/dbix-class-resultset-recursiveupdate — pull request #10 by bokutin (2014-06-07 10:35:40+00:00)
Title: m2m update time, use the fk_column instead of foreign_rel.

Hello.

I encounter a bug.
I occurred while DBIx::Class::ResultSet::RecursiveUpdate::Functions::recursive_update() called from HTML::FormHandler::TraitFor::Model::DBIC::update_model().

When m2m, If you do not specify a fk_column instead foreign_rel, SELECT fails, it attempts to INSERT, and an exception to this rule by unique constraints.

.... cataslyt console ....
SELECT "me"."id", "me"."name", "me"."country_id" FROM "region" "me" WHERE ( "me"."id" = ? ): '1'
INSERT INTO "contractor_region" ( "contractor_id", "region_id") VALUES ( ?, ? ): '22', '1'
ROLLBACK

.... catalyst error on browser ....
DBI Exception: DBD::Pg::st execute failed: ERROR: duplicate key value violates unique constraint "contractor_region_pkey"
DETAIL: Key (contractor_id, region_id)=(22, 1) already exists. [for Statement "INSERT INTO "contractor_region" ( "contractor_id", "region_id") VALUES ( ?, ? )" with ParamValues: 1='22', 2='1'] at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/DBIx/Class/Schema.pm line 1081.
DBIx::Class::Schema::throw_exception('MyApp::Schema=HASH(0x7fbda42baee0)', 'DBI Exception: DBD::Pg::st execute failed: ERROR: duplicate ...') called at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/DBIx/Class/Storage.pm line 112
DBIx::Class::Storage::throw_exception('DBIx::Class::Storage::DBI::Pg=HASH(0x7fbda24677d0)', 'DBI Exception: DBD::Pg::st execute failed: ERROR: duplicate ...') called at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/DBIx/Class/Storage/DBI.pm line 1466
DBIx::Class::Storage::DBI::__ANON__('DBD::Pg::st execute failed: ERROR: duplicate key value viola...', 'DBI::st=HASH(0x1071f4be0)', undef) called at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/DBIx/Class/Storage/DBI.pm line 1834
DBIx::Class::Storage::DBI::_dbh_execute('DBIx::Class::Storage::DBI::Pg=HASH(0x7fbda24677d0)', 'DBI::db=HASH(0x7fbdaeed24c8)', 'INSERT INTO "contractor_region" ( "contractor_id", "region_id...', 'ARRAY(0x1071f7688)', 'ARRAY(0x10723d0f0)') called at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/DBIx/Class/Storage/DBI.pm line 832
DBIx::Class::Storage::DBI::dbh_do('DBIx::Class::Storage::DBI::Pg=HASH(0x7fbda24677d0)', '_dbh_execute', 'INSERT INTO "contractor_region" ( "contractor_id", "region_id...', 'ARRAY(0x1071f7688)', 'ARRAY(0x10723d0f0)') called at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/DBIx/Class/Storage/DBI.pm line 1815
DBIx::Class::Storage::DBI::_execute('DBIx::Class::Storage::DBI::Pg=HASH(0x7fbda24677d0)', 'insert', 'DBIx::Class::ResultSource::Table=HASH(0x7fbdaeddd068)', 'HASH(0x1071f7e78)', undef) called at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/DBIx/Class/Storage/DBI.pm line 1977
DBIx::Class::Storage::DBI::insert('DBIx::Class::Storage::DBI::Pg=HASH(0x7fbda24677d0)', 'DBIx::Class::ResultSource::Table=HASH(0x7fbdaeddd068)', 'HASH(0x1071e9260)') called at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/DBIx/Class/Row.pm line 405
DBIx::Class::Row::insert('MyApp::Schema::Result::ContractorRegion=HASH(0x7fbdadf0dc28)') called at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/DBIx/Class/DynamicDefault.pm line 103
DBIx::Class::DynamicDefault::insert('MyApp::Schema::Result::ContractorRegion=HASH(0x7fbdadf0dc28)') called at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/DBIx/Class/Row.pm line 1312
DBIx::Class::Row::update_or_insert('MyApp::Schema::Result::ContractorRegion=HASH(0x7fbdadf0dc28)') called at /Users/bokutin/code/reading/dbix-class-resultset-recursiveupdate/lib/DBIx/Class/ResultSet/RecursiveUpdate.pm line 210
DBIx::Class::ResultSet::RecursiveUpdate::Functions::recursive_update('resultset', 'MyApp::Base::DBIx::Class::ResultSet=HASH(0x7fbda43ce130)', 'updates', 'HASH(0x1071a6420)', 'resolved', 'HASH(0x1071f7808)') called at /Users/bokutin/code/reading/dbix-class-resultset-recursiveupdate/lib/DBIx/Class/ResultSet/RecursiveUpdate.pm line 339
DBIx::Class::ResultSet::RecursiveUpdate::Functions::_update_relation('MyApp::Base::DBIx::Class::ResultSet=HASH(0x10729c080)', 'contractor_regions', 'ARRAY(0x7fbdadf32520)', 'MyApp::Schema::Result::Contractor=HASH(0x7fbdaeeee4e8)', undef) called at /Users/bokutin/code/reading/dbix-class-resultset-recursiveupdate/lib/DBIx/Class/ResultSet/RecursiveUpdate.pm line 253
DBIx::Class::ResultSet::RecursiveUpdate::Functions::recursive_update('object', 'MyApp::Schema::Result::Contractor=HASH(0x7fbdaeeee4e8)', 'unknown_params_ok', 1, 'updates', 'HASH(0x7fbda5b59a90)', 'resultset', 'MyApp::Base::DBIx::Class::ResultSet=HASH(0x10729c080)') called at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/HTML/FormHandler/TraitFor/Model/DBIC.pm line 85

It's running well now, fk_column and foreign_rel is because it is the same happens.

@@ t/update_introspectable_m2m.t
my $dvd_rs = $schema->resultset('Dvd');

@@ t/lib/DBSchema/Result/Dvd.pm
__PACKAGE__->has_many('dvdtags', 'Dvdtag', { 'foreign.dvd' => 'self.dvd_id' });
__PACKAGE__->many_to_many('tags', 'dvdtags' => 'tag');

@@ t/lib/DBSchema/Result/Dvdtag.pm
__PACKAGE__->add_columns(
"dvd" => { data_type => 'integer' },
"tag" => { data_type => 'integer' },
);
__PACKAGE__->belongs_to("tag", "DBSchema::Result::Tag", { id => "tag" });

##########################################################
foreign_rel is "tag". fk_column is "tag". They are the same.

By adding a test, I have to reproduce the bug.
And I wrote a patch.

I'm glad when it was no problem, and you can be a merge.

Cheers,
Tomohiro Hosaka