Subject: | DCSL crashes with some schemas |
DBIx::Class::Schema::Loader (DCSL) crashes when processing some schemas
that contain columns with names that clash with DBIx::Class methods. A
failing test is attached (name-clash-test.pl). The test script creates a
SQLite database and then uses DCSL to process it. Once the database has
been created, the crash can be reproduced simply by running:
dbicdump test::schema 'dbi:SQLite:dbname=name-clash-test.dbfile'
The data that causes DCSL to crash is a schema that contains a foreign
key column definition 'belongs_to' (it predates DBIC). The same table
also has another column that is a foreign key. DCSL emits code that
injects a belongs_to relationship called 'belongs_to' into the package
and then tries to execute it as though it were the built-in method of
the same name.
DCSL::Base has a method called _resolve_col_accessor_collisions that
notices naming conflicts. But DCSL::RelBuilder doesn't make use of that
information. A possible fix would be to make it do so or otherwise make
an equivalent check before generating code.
But in my view a better solution would be for DCSL to emit warnings
about name clashes and not generate code at all. Quietly generating code
that isn't going to work even if it doesn't immediately crash, as it
does in most cases at present, is not very helpful to the user. A patch
for a half-way house that just emits the warnings is also attached.
There also needs to be a POD change, probably in the Known Issues
section of DBIx::Class::Schema::Loader::Base. Perhaps something like:
=head2 Accessor Name Conflicts
Because DBIx::Class accessor and relationship methods are in the same
class as the meta-methods used to generate them (add_columns() and
belongs_to() etc) it is possible although unusual for there to be a
conflict between the name of a column in a schema and the name of one of
the built-in methods. DBIx::Class::Schema::Loader detects these
conflicts and prints warnings. The generated schema is unusable and
requires manual editing before use, which must be repeated each time
DBIx::Class::Schema::Loader is run. In rare cases,
DBIx::Class::Schema::Loader may crash after it has generated the schema.
If possible, the recommended solution is to change the name of the
column in the database to avoid the conflict.
Another question is how to produce a functioning Result class, once one
is made aware of the need for customisation. I have found
custom_column_info but its POD says "Hook for adding *extra* attributes
to the column_info for a column". I have found that I can use it to
change existing attributes, specifically 'accessor', so code like that
below appears to generate usable classes, even though the replaced
accessor name doesn't appear in the generated relationship definition:
use DBIx::Class::Schema::Loader qw/ make_schema_at /;
sub my_custom_column_info
{
my ($table_name, $column_name, $column_info) = @_;
warn "($table_name, $column_name, $column_info)\n";
if ($column_name eq 'belongs_to')
{
return { accessor => 'belong_him' };
}
}
make_schema_at( 'NameClash2::Schema',
{
debug => 1,
dump_directory => '.',
custom_column_info => \&my_custom_column_info,
},
[
"dbi:SQLite:dbname=$file",
'',
'',
],
);
It's then possible in a script using the generated classes to say
$t2_obj->belong_him and get a T1 object in return.
Is that a good way to change accessor names or is there a better way? I
couldn't find any documentation and am concerned whether it works in all
cases and will keep working in the future?
Subject: | name-clash-test.pl |
#!/usr/bin/perl
use strict;
use warnings;
=head1 NAME
name-clash-test.pl - crash DBIx::Class::Schema::Loader when there is a
column name that collides with a built-in method name
2010-10-28
=cut
my $file = 'name-clash-test.dbfile';
my $ddl1 = '
CREATE TABLE t1 (
id INTEGER PRIMARY KEY,
noise TEXT
);';
my $ddl2 = '
CREATE TABLE t2 (
id INTEGER PRIMARY KEY,
second_foreign_key INTEGER,
belongs_to INTEGER,
FOREIGN KEY (second_foreign_key) REFERENCES t1(id),
FOREIGN KEY (belongs_to) REFERENCES t1(id)
);';
my $sql1 = 'INSERT INTO t1 (noise) VALUES ("art of");';
my $sql2 = 'INSERT INTO t2 (belongs_to) VALUES (1);';
unlink $file if -e $file;
use DBI;
# DBI->trace(1);
my $dbh = DBI->connect("dbi:SQLite:dbname=$file", '', '') or die;
$dbh->do($ddl1) or die;
$dbh->do($ddl2) or die;
$dbh->do($sql1) or die;
$dbh->do($sql2) or die;
$dbh->disconnect or die;
use DBIx::Class::Schema::Loader qw/ make_schema_at /;
make_schema_at( 'NameClash::Schema',
{
debug => 1,
dump_directory => '.',
},
[
"dbi:SQLite:dbname=$file",
'',
'',
],
);
Subject: | patch |
Message body not shown because it is not plain text.