turn off cascade_delete/copy, set on_delete/update
Rafael Kitover [Tue, 16 Mar 2010 00:15:45 +0000 (20:15 -0400)]
Changes
lib/DBIx/Class/Schema/Loader/Manual/UpgradingFromV4.pod
lib/DBIx/Class/Schema/Loader/RelBuilder.pm
lib/DBIx/Class/Schema/Loader/RelBuilder/Compat/v0_040.pm
t/backcompat/0.04006/lib/dbixcsl_common_tests.pm
t/lib/dbixcsl_common_tests.pm

diff --git a/Changes b/Changes
index 5374c13..ec5ecad 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,8 @@
 Revision history for Perl extension DBIx::Class::Schema::Loader
 
+        - cascade_delete and cascade_copy are turned off for has_many/might_have
+          by default, and belongs_to has on_delete => 'CASCADE' and on_update =>
+          'CASCADE' by default, overridable via relationship_attrs
         - set inflate_datetime => 1 for 'AS getdate()' computed columns in
           Sybase
         - preliminary Firebird support
index 391fafc..21dc9e3 100644 (file)
@@ -28,6 +28,12 @@ It will also more correctly infer the relationship type, e.g. some relationships
 that were previously detected as a C<has_many> will now be a C<might_have>
 (when it detects a unique constraint on the foreign key column.)
 
+Also C<cascade_delete> and C<cascade_copy> are turned off for by default for
+C<has_many> and C<might_have> relationships, while C<belongs_to> relationships
+are created with C<< on_delete => 'CASCADE' >> and C<< on_update => 'CASCADE' >>
+by default. This is overridable via
+L<relationship_attrs|DBIx::Class::Schema::Loader::Base/relationship_attrs>.
+
 =item *
 
 moniker_map
index 0a86d60..359c9a7 100644 (file)
@@ -156,15 +156,33 @@ sub _to_S {
     return Lingua::EN::Inflect::Number::to_S($name);
 }
 
+sub _default_relationship_attrs { +{
+    has_many => {
+        cascade_delete => 0,
+        cascade_copy   => 0,
+    },
+    might_have => {
+        cascade_delete => 0,
+        cascade_copy   => 0,
+    },
+    belongs_to => {
+        on_delete => 'CASCADE',
+        on_update => 'CASCADE',
+    },
+} }
+
 # accessor for options to be passed to each generated relationship
 # type.  take single argument, the relationship type name, and returns
 # either a hashref (if some options are set), or nothing
 sub _relationship_attrs {
     my ( $self, $reltype ) = @_;
     my $r = $self->{relationship_attrs};
-    return unless $r && ( $r->{all} || $r->{$reltype} );
 
-    my %composite = %{ $r->{all} || {} };
+    my %composite = (
+        %{ $self->_default_relationship_attrs->{$reltype} || {} },
+        %{ $r->{all} || {} }
+    );
+
     if( my $specific = $r->{$reltype} ) {
        while( my ($k,$v) = each %$specific ) {
            $composite{$k} = $v;
index adb5f30..cc2653c 100644 (file)
@@ -6,6 +6,8 @@ use Class::C3;
 
 use base 'DBIx::Class::Schema::Loader::RelBuilder';
 
+sub _default_relationship_attrs { +{} }
+
 sub _to_PL {
     my ($self, $name) = @_;
 
index 7ab3f98..4d51475 100644 (file)
@@ -43,7 +43,7 @@ sub _monikerize {
 sub run_tests {
     my $self = shift;
 
-    plan tests => 91;
+    plan tests => 99;
 
     $self->create();
 
@@ -255,7 +255,7 @@ sub run_tests {
     is( $obj2->id, 2 );
 
     SKIP: {
-        skip $self->{skip_rels}, 52 if $self->{skip_rels};
+        skip $self->{skip_rels}, 61 if $self->{skip_rels};
 
         my $moniker3 = $monikers->{loader_test3};
         my $class3   = $classes->{loader_test3};
@@ -354,6 +354,31 @@ sub run_tests {
         ok ($rsobj4->result_source->has_relationship('loader_test5_from_ids'),
             "rel with preposition 'from' and _id pluralized backward-compatibly");
 
+        # check that default relationship attributes are not applied in 0.04006 mode
+        is $rsobj3->result_source->relationship_info('loader_test4zes')->{attrs}{cascade_delete}, 1,
+            'cascade_delete => 1 on has_many by default';
+
+        is $rsobj3->result_source->relationship_info('loader_test4zes')->{attrs}{cascade_copy}, 1,
+            'cascade_copy => 1 on has_many by default';
+
+        ok ((not exists $rsobj3->result_source->relationship_info('loader_test4zes')->{attrs}{on_delete}),
+            'has_many does not have on_delete');
+
+        ok ((not exists $rsobj3->result_source->relationship_info('loader_test4zes')->{attrs}{on_update}),
+            'has_many does not have on_update');
+
+        isnt $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{on_delete}, 'CASCADE',
+            "on_delete => 'CASCADE' not on belongs_to by default";
+
+        isnt $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{on_update}, 'CASCADE',
+            "on_update => 'CASCADE' not on belongs_to by default";
+
+        ok ((not exists $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{cascade_delete}),
+            'belongs_to does not have cascade_delete');
+
+        ok ((not exists $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{cascade_copy}),
+            'belongs_to does not have cascade_copy');
+
         # find on multi-col pk
         my $obj5 = $rsobj5->find({id1 => 1, id2 => 1});
         is( $obj5->id2, 1 );
index 97de888..d8dc3f5 100644 (file)
@@ -60,29 +60,6 @@ sub _monikerize {
     return undef;
 }
 
-sub _custom_column_info {
-    my ( $table_name, $column_name, $column_info ) = @_;
-
-    $table_name = lc ( $table_name );
-    $column_name = lc ( $column_name );
-
-    if ( $table_name eq 'loader_test35' 
-        and $column_name eq 'an_int' 
-    ){
-        return { is_numeric => 1 }
-    }
-    # Set inflate_datetime or  inflate_date to check 
-    #   datetime_timezone and datetime_locale
-    if ( $table_name eq 'loader_test36' ){
-        return { inflate_datetime => 1 } if 
-            ( $column_name eq 'b_char_as_data' );
-        return { inflate_date => 1 } if 
-            ( $column_name eq 'c_char_as_data' );
-    }
-
-    return;
-}
-
 sub run_tests {
     my $self = shift;
 
@@ -97,7 +74,7 @@ sub run_tests {
         }
     }
 
-    plan tests => @connect_info * (159 + ($self->{extra}->{count} || 0));
+    plan tests => @connect_info * (171 + ($self->{extra}->{count} || 0));
 
     foreach my $info_idx (0..$#connect_info) {
         my $info = $connect_info[$info_idx];
@@ -392,7 +369,7 @@ sub test_schema {
     );
 
     SKIP: {
-        skip $self->{skip_rels}, 101 if $self->{skip_rels};
+        skip $self->{skip_rels}, 113 if $self->{skip_rels};
 
         my $moniker3 = $monikers->{loader_test3};
         my $class3   = $classes->{loader_test3};
@@ -532,6 +509,43 @@ sub test_schema {
         ok ($rsobj4->result_source->has_relationship('loader_test5s_from'),
             "rel with preposition 'from' pluralized correctly");
 
+        # check default relationship attributes
+        is $rsobj3->result_source->relationship_info('loader_test4zes')->{attrs}{cascade_delete}, 0,
+            'cascade_delete => 0 on has_many by default';
+
+        is $rsobj3->result_source->relationship_info('loader_test4zes')->{attrs}{cascade_copy}, 0,
+            'cascade_copy => 0 on has_many by default';
+
+        ok ((not exists $rsobj3->result_source->relationship_info('loader_test4zes')->{attrs}{on_delete}),
+            'has_many does not have on_delete');
+
+        ok ((not exists $rsobj3->result_source->relationship_info('loader_test4zes')->{attrs}{on_update}),
+            'has_many does not have on_update');
+
+        is $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{on_delete}, 'CASCADE',
+            "on_delete => 'CASCADE' on belongs_to by default";
+
+        is $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{on_update}, 'CASCADE',
+            "on_update => 'CASCADE' on belongs_to by default";
+
+        ok ((not exists $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{cascade_delete}),
+            'belongs_to does not have cascade_delete');
+
+        ok ((not exists $rsobj4->result_source->relationship_info('fkid_singular')->{attrs}{cascade_copy}),
+            'belongs_to does not have cascade_copy');
+
+        is $rsobj27->result_source->relationship_info('loader_test28')->{attrs}{cascade_delete}, 0,
+            'cascade_delete => 0 on might_have by default';
+
+        is $rsobj27->result_source->relationship_info('loader_test28')->{attrs}{cascade_copy}, 0,
+            'cascade_copy => 0 on might_have by default';
+
+        ok ((not exists $rsobj27->result_source->relationship_info('loader_test28')->{attrs}{on_delete}),
+            'might_have does not have on_delete');
+
+        ok ((not exists $rsobj27->result_source->relationship_info('loader_test28')->{attrs}{on_update}),
+            'might_have does not have on_update');
+
         # find on multi-col pk
         my $obj5 = 
            eval { $rsobj5->find({id1 => 1, iD2 => 1}) } ||
@@ -1448,6 +1462,29 @@ sub drop_tables {
     $dbh->disconnect;
 }
 
+sub _custom_column_info {
+    my ( $table_name, $column_name, $column_info ) = @_;
+
+    $table_name = lc ( $table_name );
+    $column_name = lc ( $column_name );
+
+    if ( $table_name eq 'loader_test35' 
+        and $column_name eq 'an_int' 
+    ){
+        return { is_numeric => 1 }
+    }
+    # Set inflate_datetime or  inflate_date to check 
+    #   datetime_timezone and datetime_locale
+    if ( $table_name eq 'loader_test36' ){
+        return { inflate_datetime => 1 } if 
+            ( $column_name eq 'b_char_as_data' );
+        return { inflate_date => 1 } if 
+            ( $column_name eq 'c_char_as_data' );
+    }
+
+    return;
+}
+
 sub DESTROY {
     my $self = shift;
     unless ($ENV{SCHEMA_LOADER_TESTS_NOCLEANUP}) {