Revision history for DBIx::Class
+ - DBIx::Class::InflateColumn::File entered deprecated state
- Fix regression where SQL files with comments were not
handled properly by ::Schema::Versioned.
- Fix regression on not properly throwing when $obj->relationship
is unresolvable
- Add has_relationship method to row objects
- Fix regression in set_column on PK-less objects
- - Fix for SQLite to ignore the { for => ... } attribute
+ - Add POD about the significance of PK columns
+ - Fix for SQLite to ignore the (unsupported) { for => ... }
+ attribute
+ - Fix ambiguity in default directory handling of create_ddl_dir
+ (RT#54063)
0.08120 2010-02-24 08:58:00 (UTC)
- Make sure possibly overwritten deployment_statements methods in
use File::Copy;
use Path::Class;
+use Carp::Clan qw/^DBIx::Class/;
+carp 'InflateColumn::File has entered a deprecation cycle. This component '
+ .'has a number of architectural deficiencies that can quickly drive '
+ .'your filesystem and database out of sync and is not recommended '
+ .'for further use. It will be retained for backwards '
+ .'compatibility, but no new functionality patches will be accepted. '
+ .'Please consider using the much more mature and actively maintained '
+ .'DBIx::Class::InflateColumn::FS. You can set the environment variable '
+ .'DBIC_IC_FILE_NOWARN to a true value to disable this warning.'
+unless $ENV{DBIC_IC_FILE_NOWARN};
+
__PACKAGE__->load_components(qw/InflateColumn/);
sub register_column {
=head1 NAME
-DBIx::Class::InflateColumn::File - map files from the Database to the filesystem.
+DBIx::Class::InflateColumn::File - DEPRECATED (superseded by DBIx::Class::InflateColumn::FS)
+
+=head2 Deprecation Notice
+
+ This component has a number of architectural deficiencies that can quickly
+ drive your filesystem and database out of sync and is not recommended for
+ further use. It will be retained for backwards compatibility, but no new
+ functionality patches will be accepted. Please consider using the much more
+ mature and actively supported DBIx::Class::InflateColumn::FS. You can set
+ the environment variable DBIC_IC_FILE_NOWARN to a true value to disable
+ this warning.
=head1 SYNOPSIS
sub insert {
my ( $self, @args ) = @_;
$self->next::method(@args);
- $self->cds->new({})->fill_from_artist($self)->insert;
+ $self->create_related ('cds', \%initial_cd_data );
return $self;
}
-where C<fill_from_artist> is a method you specify in C<CD> which sets
-values in C<CD> based on the data in the C<Artist> object you pass in.
+If you want to wrap the two inserts in a transaction (for consistency,
+an excellent idea), you can use the awesome
+L<DBIx::Class::Storage::TxnScopeGuard>:
+
+ sub insert {
+ my ( $self, @args ) = @_;
+
+ my $guard = $self->result_source->schema->txn_scope_guard;
+
+ $self->next::method(@args);
+ $self->create_related ('cds', \%initial_cd_data );
+
+ $guard->commit;
+
+ return $self
+ }
+
=head2 Wrapping/overloading a column accessor
=head1 NOTES
+=head2 The Significance and Importance of Primary Keys
+
+The concept of a L<primary key|DBIx::Class::ResultSource/set_primary_key> in
+DBIx::Class warrants special discussion. The formal definition (which somewhat
+resembles that of a classic RDBMS) is I<a unique constraint that is least
+likely to change after initial row creation>. However this is where the
+similarity ends. Any time you call a CRUD operation on a row (e.g.
+L<delete|DBIx::Class::Row/delete>,
+L<update|DBIx::Class::Row/update>,
+L<discard_changes|DBIx::Class::Row/discard_changes>,
+etc.) DBIx::Class will use the values of of the
+L<primary key|DBIx::Class::ResultSource/set_primary_key> columns to populate
+the C<WHERE> clause necessary to accomplish the operation. This is why it is
+important to declare a L<primary key|DBIx::Class::ResultSource/set_primary_key>
+on all your result sources B<even if the underlying RDBMS does not have one>.
+In a pinch one can always declare each row identifiable by all its columns:
+
+ __PACKAGE__->set_primary_keys (__PACKAGE__->columns);
+
+Note that DBIx::Class is smart enough to store a copy of the PK values before
+any row-object changes take place, so even if you change the values of PK
+columns the C<WHERE> clause will remain correct.
+
+If you elect not to declare a C<primary key>, DBIx::Class will behave correctly
+by throwing exceptions on any row operation that relies on unique identifiable
+rows. If you inherited datasets with multiple identical rows in them, you can
+still operate with such sets provided you only utilize
+L<DBIx::Class::ResultSet> CRUD methods:
+L<search|DBIx::Class::ResultSet/search>,
+L<update|DBIx::Class::ResultSet/update>,
+L<delete|DBIx::Class::ResultSet/delete>
+
+For example, the following would not work (assuming C<People> does not have
+a declared PK):
+
+ my $row = $schema->resultset('People')
+ ->search({ last_name => 'Dantes' })
+ ->next;
+ $row->update({ children => 2 }); # <-- exception thrown because $row isn't
+ # necessarily unique
+
+So instead the following should be done:
+
+ $schema->resultset('People')
+ ->search({ last_name => 'Dantes' })
+ ->update({ children => 2 }); # <-- update's ALL Dantes to have children of 2
+
=head2 Problems on RHEL5/CentOS5
There used to be an issue with the system perl on Red Hat Enterprise
sub _ident_values {
my ($self) = @_;
+
my (@ids, @missing);
for ($self->_pri_cols) {
Additionally, defines a L<unique constraint|add_unique_constraint>
named C<primary>.
-The primary key columns are used by L<DBIx::Class::PK::Auto> to
-retrieve automatically created values from the database. They are also
-used as default joining columns when specifying relationships, see
-L<DBIx::Class::Relationship>.
+Note: you normally do want to define a primary key on your sources
+B<even if the underlying database table does not have a primary key>.
+See
+L<DBIx::Class::Intro/The Significance and Importance of Primary Keys>
+for more info.
=cut
according to L</in_storage>.
This method issues an SQL UPDATE query to commit any changes to the
-object to the database if required.
+object to the database if required (see L</get_dirty_columns>).
+It throws an exception if a proper WHERE clause uniquely identifying
+the database row can not be constructed (see
+L<significance of primary keys|DBIx::Class::Manual::Intro/The Significance and Importance of Primary Keys>
+for more details).
Also takes an optional hashref of C<< column_name => value> >> pairs
to update on the object first. Be aware that the hashref will be
=back
Throws an exception if the object is not in the database according to
-L</in_storage>. Runs an SQL DELETE statement using the primary key
-values to locate the row.
+L</in_storage>. Also throws an exception if a proper WHERE clause
+uniquely identifying the database row can not be constructed (see
+L<significance of primary keys|DBIx::Class::Manual::Intro/The Significance and Importance of Primary Keys>
+for more details).
The object is still perfectly usable, but L</in_storage> will
now return 0 and the object must be reinserted using L</insert>
=back
Fetches a fresh copy of the Row object from the database and returns it.
-
-If passed the \%attrs argument, will first apply these attributes to
+Throws an exception if a proper WHERE clause identifying the database row
+can not be constructed (i.e. if the original object does not contain its
+entire
+ L<primary key|DBIx::Class::Manual::Intro/The Significance and Importance of Primary Keys>
+). If passed the \%attrs argument, will first apply these attributes to
the resultset used to find the row.
This copy can then be used to compare to an existing row object, to
=head2 discard_changes ($attrs)
Re-selects the row from the database, losing any changes that had
-been made.
+been made. Throws an exception if a proper WHERE clause identifying
+the database row can not be constructed (i.e. if the original object
+does not contain its entire
+L<primary key|DBIx::Class::Manual::Intro/The Significance and Importance of Primary Keys>
+).
This method can also be used to refresh from storage, retrieving any
changes made since the row was last read from storage.
else {
# try to use dsn to not require being connected, the driver may still
# force a connection in _rebless to determine version
- ($driver) = $self->_dbi_connect_info->[0] =~ /dbi:([^:]+):/i;
+ # (dsn may not be supplied at all if all we do is make a mock-schema)
+ my $dsn = $self->_dbi_connect_info->[0] || '';
+ ($driver) = $dsn =~ /dbi:([^:]+):/i;
}
}
- my $storage_class = "DBIx::Class::Storage::DBI::${driver}";
- if ($self->load_optional_class($storage_class)) {
- mro::set_mro($storage_class, 'c3');
- bless $self, $storage_class;
- $self->_rebless();
+ if ($driver) {
+ my $storage_class = "DBIx::Class::Storage::DBI::${driver}";
+ if ($self->load_optional_class($storage_class)) {
+ mro::set_mro($storage_class, 'c3');
+ bless $self, $storage_class;
+ $self->_rebless();
+ }
}
}
sub create_ddl_dir {
my ($self, $schema, $databases, $version, $dir, $preversion, $sqltargs) = @_;
- if(!$dir || !-d $dir) {
+ unless ($dir) {
carp "No directory given, using ./\n";
- $dir = "./";
+ $dir = './';
}
+
+ $self->throw_exception ("Directory '$dir' does not exist\n") unless(-d $dir);
+
$databases ||= ['MySQL', 'SQLite', 'PostgreSQL'];
$databases = [ $databases ] if(ref($databases) ne 'ARRAY');
use strict;
-use warnings;
+use warnings;
use Test::More;
use lib qw(t/lib);
+
+# inject IC::File into the result baseclass for testing
+BEGIN {
+ $ENV{DBIC_IC_FILE_NOWARN} = 1;
+ require DBICTest::BaseResult;
+ DBICTest::BaseResult->load_components (qw/InflateColumn::File/);
+}
+
+
use DBICTest;
use File::Compare;
use Path::Class qw/file/;
-my $schema = DBICTest->init_schema();
+my $schema = DBICTest->init_schema;
plan tests => 10;
use base qw/DBICTest::BaseResult/;
use File::Temp qw/tempdir/;
-__PACKAGE__->load_components(qw/InflateColumn::File/);
-
__PACKAGE__->table('file_columns');
__PACKAGE__->add_columns(