Merge 'trunk' into 'storage-interbase'
Rafael Kitover [Sat, 6 Mar 2010 17:07:56 +0000 (17:07 +0000)]
r23590@hlagh (orig r8900):  ribasushi | 2010-03-06 06:26:10 -0500
More checks for weird usage of _determine_driver (maint/gen-schema)
r23593@hlagh (orig r8903):  ribasushi | 2010-03-06 06:30:55 -0500
 r8422@Thesaurus (orig r8409):  ribasushi | 2010-01-22 11:14:57 +0100
 Branches for some stuff
 r8477@Thesaurus (orig r8464):  ribasushi | 2010-01-28 12:26:40 +0100
 RT#52681
 r8478@Thesaurus (orig r8465):  ribasushi | 2010-01-28 12:41:25 +0100
 Deprecate IC::File
 r8479@Thesaurus (orig r8466):  ribasushi | 2010-01-28 12:41:56 +0100
 Deprecate IC::File(2)
 r8487@Thesaurus (orig r8474):  ribasushi | 2010-01-30 13:11:18 +0100
 Draft PK explanation
 r8488@Thesaurus (orig r8475):  frew | 2010-01-30 21:19:30 +0100
 clarify PK stuff in intro just a bit
 r8489@Thesaurus (orig r8476):  frew | 2010-01-30 21:24:21 +0100
 no first in POD
 r8910@Thesaurus (orig r8897):  rabbit | 2010-03-06 11:37:12 +0100
 Improve POD about PKs and why they matter
 r8912@Thesaurus (orig r8899):  rabbit | 2010-03-06 11:42:41 +0100
 One more PODlink
 r8915@Thesaurus (orig r8902):  rabbit | 2010-03-06 12:27:29 +0100
 Fully deprecate IC::File

r23596@hlagh (orig r8906):  ribasushi | 2010-03-06 06:44:58 -0500
Fix RT54063
r23597@hlagh (orig r8907):  ribasushi | 2010-03-06 07:18:01 -0500
me-- not thinking

Changes
lib/DBIx/Class/InflateColumn/File.pm
lib/DBIx/Class/Manual/Cookbook.pod
lib/DBIx/Class/Manual/Intro.pod
lib/DBIx/Class/PK.pm
lib/DBIx/Class/ResultSource.pm
lib/DBIx/Class/Row.pm
lib/DBIx/Class/Storage/DBI.pm
t/inflate/file_column.t
t/lib/DBICTest/Schema/FileColumn.pm

diff --git a/Changes b/Changes
index 39a1d13..d4bced8 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,12 +1,17 @@
 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
index 3d67914..951b76e 100644 (file)
@@ -7,6 +7,17 @@ use File::Path;
 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 {
@@ -107,7 +118,17 @@ sub _save_file_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
 
index c84df81..16fa647 100644 (file)
@@ -1821,12 +1821,27 @@ You can accomplish this by overriding C<insert> on your objects:
   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
 
index 55a7463..18347d8 100644 (file)
@@ -401,6 +401,53 @@ L<DBIx::Class::ResultSet/ATTRIBUTES>.
 
 =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
index a25c431..c4d3b93 100644 (file)
@@ -37,6 +37,7 @@ sub id {
 
 sub _ident_values {
   my ($self) = @_;
+
   my (@ids, @missing);
 
   for ($self->_pri_cols) {
index b849696..ebbf960 100644 (file)
@@ -465,10 +465,11 @@ called after L</add_columns>.
 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
 
index ebde5d9..554d346 100644 (file)
@@ -439,7 +439,11 @@ Throws an exception if the row object is not yet in the database,
 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
@@ -512,8 +516,10 @@ sub update {
 =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>
@@ -1275,8 +1281,11 @@ sub register_column {
 =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
@@ -1311,7 +1320,11 @@ sub get_from_storage {
 =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.
index a1e3055..4d4af27 100644 (file)
@@ -950,15 +950,19 @@ sub _determine_driver {
         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();
+        }
       }
     }
 
@@ -2253,10 +2257,13 @@ them.
 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');
 
index 639b12d..7f0eddd 100644 (file)
@@ -1,13 +1,22 @@
 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;
 
index 82fcebd..046e7c2 100644 (file)
@@ -6,8 +6,6 @@ use warnings;
 use base qw/DBICTest::BaseResult/;
 use File::Temp qw/tempdir/;
 
-__PACKAGE__->load_components(qw/InflateColumn::File/);
-
 __PACKAGE__->table('file_columns');
 
 __PACKAGE__->add_columns(