Improve POD about PKs and why they matter
Peter Rabbitson [Sat, 6 Mar 2010 10:37:12 +0000 (10:37 +0000)]
lib/DBIx/Class/Manual/Intro.pod
lib/DBIx/Class/Row.pm

index 2728c5a..8e4b7fd 100644 (file)
@@ -403,24 +403,23 @@ 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. While in a RDBMS you can safely change any column within a
-row, you can not do the same in DBIC because B<you will be breaking the tie
-between a specific L<DBIx::Class::Row> object and a specific row in your
-database>. Any time you call a CRUD operation on a row (e.g.
+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 B<current values> of the
+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:
+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
@@ -430,18 +429,20 @@ 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:
+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;
+                   ->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
+ $schema->resultset('People')
+         ->search({ last_name => 'Dantes' })
+          ->update({ children => 2 }); # <-- update's ALL Dantes to have children of 2
 
 =head2 Problems on RHEL5/CentOS5
 
index a77615b..28a28a8 100644 (file)
@@ -443,7 +443,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
@@ -515,8 +519,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>
@@ -1280,8 +1286,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.