Merge 'trunk' into 'storage-interbase'
Rafael Kitover [Sun, 7 Mar 2010 10:14:08 +0000 (10:14 +0000)]
r23611@hlagh (orig r8921):  ribasushi | 2010-03-06 18:52:50 -0500
Cascading delete needs a guard to remain atomic
r23613@hlagh (orig r8923):  ribasushi | 2010-03-06 20:35:49 -0500
Fix the docs for select/as
r23614@hlagh (orig r8924):  ribasushi | 2010-03-06 20:58:09 -0500
Unmark Opt::Deps experimental and add extra method as per RT55211
r23615@hlagh (orig r8925):  ribasushi | 2010-03-06 21:22:07 -0500
Switch NoTab/EOL checks to Opt::Deps
Enable NoTab checks
Disable EOL checks
r23616@hlagh (orig r8926):  ribasushi | 2010-03-07 04:23:23 -0500
Cleanup a bit

Changes
Makefile.PL
lib/DBIx/Class/Manual/FAQ.pod
lib/DBIx/Class/Optional/Dependencies.pm
lib/DBIx/Class/Relationship/CascadeActions.pm
lib/DBIx/Class/ResultSet.pm
t/06notabs.t
t/07eol.t
t/81transactions.t
t/87ordered.t
t/lib/DBICTest/Schema/Employee.pm

diff --git a/Changes b/Changes
index d4bced8..4495626 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,6 +1,10 @@
 Revision history for DBIx::Class
 
         - DBIx::Class::InflateColumn::File entered deprecated state
+        - DBIx::Class::Optional::Dependencies left experimental state
+        - Add req_group_list to Opt::Deps (RT#55211)
+        - Cascading delete/update are now wrapped in a transaction
+          for atomicity
         - Fix regression where SQL files with comments were not
           handled properly by ::Schema::Versioned.
         - Fix regression on not properly throwing when $obj->relationship
index d14e185..9d087b2 100644 (file)
@@ -78,7 +78,7 @@ EOW
   require DBIx::Class::Optional::Dependencies;
   $reqs->{test_requires} = {
     %{$reqs->{test_requires}},
-    %{DBIx::Class::Optional::Dependencies->_all_optional_requirements},
+    map { %$_ } (values %{DBIx::Class::Optional::Dependencies->req_group_list}),
   };
 }
 
index 9b98d4c..464040d 100644 (file)
@@ -182,15 +182,9 @@ attribute. See L<DBIx::Class::ResultSet/order_by>.
 
 =item .. sort my results based on fields I've aliased using C<as>?
 
-You don't. You'll need to supply the same functions/expressions to
-C<order_by>, as you did to C<select>.
-
-To get "fieldname AS alias" in your SQL, you'll need to supply a
-literal chunk of SQL in your C<select> attribute, such as:
-
- ->search({}, { select => [ \'now() AS currenttime'] })
-
-Then you can use the alias in your C<order_by> attribute.
+You didn't alias anything, since L<as|DBIx::Class::ResultSet/as>
+B<has nothing to do> with the produced SQL. See
+L<DBIx::Class::ResultSet/select> for details.
 
 =item .. group the results of my search?
 
@@ -199,15 +193,7 @@ attribute, see L<DBIx::Class::ResultSet/group_by>.
 
 =item .. group my results based on fields I've aliased using C<as>?
 
-You don't. You'll need to supply the same functions/expressions to
-C<group_by>, as you did to C<select>.
-
-To get "fieldname AS alias" in your SQL, you'll need to supply a
-literal chunk of SQL in your C<select> attribute, such as:
-
- ->search({}, { select => [ \'now() AS currenttime'] })
-
-Then you can use the alias in your C<group_by> attribute.
+You don't. See the explanation on ordering by an alias above.
 
 =item .. filter the results of my search?
 
index f8800a8..bc262eb 100644 (file)
@@ -90,13 +90,13 @@ my $reqs = {
 
   test_notabs => {
     req => {
-      #'Test::NoTabs'              => '0.9',
+      'Test::NoTabs'              => '0.9',
     },
   },
 
   test_eol => {
     req => {
-      #'Test::EOL'                 => '0.6',
+      'Test::EOL'                 => '0.6',
     },
   },
 
@@ -195,10 +195,6 @@ my $reqs = {
 };
 
 
-sub _all_optional_requirements {
-  return { map { %{ $reqs->{$_}{req} || {} } } (keys %$reqs) };
-}
-
 sub req_list_for {
   my ($class, $group) = @_;
 
@@ -281,7 +277,11 @@ sub _check_deps {
   }
 }
 
-# This is to be called by the author onbly (automatically in Makefile.PL)
+sub req_group_list {
+  return { map { $_ => { %{ $reqs->{$_}{req} || {} } } } (keys %$reqs) };
+}
+
+# This is to be called by the author only (automatically in Makefile.PL)
 sub _gen_pod {
   my $class = shift;
   my $modfn = __PACKAGE__ . '.pm';
@@ -289,6 +289,8 @@ sub _gen_pod {
 
   require DBIx::Class;
   my $distver = DBIx::Class->VERSION;
+  my $sqltver = $class->req_list_for ('deploy')->{'SQL::Translator'}
+    or die "Hrmm? No sqlt dep?";
 
   my @chunks = (
     <<"EOC",
@@ -303,10 +305,8 @@ sub _gen_pod {
 EOC
     '=head1 NAME',
     "$class - Optional module dependency specifications (for module authors)",
-    '=head1 SYNOPSIS (EXPERIMENTAL)',
+    '=head1 SYNOPSIS',
     <<EOS,
-B<THE USAGE SHOWN HERE IS EXPERIMENTAL>
-
 Somewhere in your build-file (e.g. L<Module::Install>'s Makefile.PL):
 
   ...
@@ -366,6 +366,17 @@ EOD
 
   push @chunks, (
     '=head1 METHODS',
+    '=head2 req_group_list',
+    '=over',
+    '=item Arguments: $none',
+    '=item Returns: \%list_of_requirement_groups',
+    '=back',
+    <<EOD,
+This method should be used by DBIx::Class packagers, to get a hashref of all
+dependencies keyed by dependency group. Each key (group name) can be supplied
+to one of the group-specific methods below.
+EOD
+
     '=head2 req_list_for',
     '=over',
     '=item Arguments: $group_name',
@@ -374,7 +385,7 @@ EOD
     <<EOD,
 This method should be used by DBIx::Class extension authors, to determine the
 version of modules a specific feature requires in the B<current> version of
-DBIx::Class. See the L<SYNOPSIS|/SYNOPSIS (EXPERIMENTAL)> for a real-world
+DBIx::Class. See the L</SYNOPSIS> for a real-world
 example.
 EOD
 
@@ -396,10 +407,10 @@ This method would normally be used by DBIx::Class core-module author, to
 indicate to the user that he needs to install specific modules before he will
 be able to use a specific feature.
 
-For example if the requirements for C<replicated> are not available, the
-returned string would look like:
+For example if some of the requirements for C<deploy> are not available,
+the returned string could look like:
 
- Moose >= 0.98, MooseX::Types >= 0.21, namespace::clean (see $class for details)
+ SQL::Translator >= $sqltver (see $class for details)
 
 The author is expected to prepend the necessary text to this message before
 returning the actual error seen by the user.
index c3a66ea..fde8f5d 100644 (file)
@@ -16,15 +16,24 @@ sub delete {
     # be handling this anyway. Assuming we have joins we probably actually
     # *could* do them, but I'd rather not.
 
-  my $ret = $self->next::method(@rest);
-
   my $source = $self->result_source;
   my %rels = map { $_ => $source->relationship_info($_) } $source->relationships;
   my @cascade = grep { $rels{$_}{attrs}{cascade_delete} } keys %rels;
-  foreach my $rel (@cascade) {
-    $self->search_related($rel)->delete_all;
+
+  if (@cascade) {
+    my $guard = $source->schema->txn_scope_guard;
+
+    my $ret = $self->next::method(@rest);
+
+    foreach my $rel (@cascade) {
+      $self->search_related($rel)->delete_all;
+    }
+
+    $guard->commit;
+    return $ret;
   }
-  return $ret;
+
+  $self->next::method(@rest);
 }
 
 sub update {
@@ -32,22 +41,31 @@ sub update {
   return $self->next::method(@rest) unless ref $self;
     # Because update cascades on a class *really* don't make sense!
 
-  my $ret = $self->next::method(@rest);
-
   my $source = $self->result_source;
   my %rels = map { $_ => $source->relationship_info($_) } $source->relationships;
   my @cascade = grep { $rels{$_}{attrs}{cascade_update} } keys %rels;
-  foreach my $rel (@cascade) {
-    next if (
-      $rels{$rel}{attrs}{accessor}
-        &&
-      $rels{$rel}{attrs}{accessor} eq 'single'
-        &&
-      !exists($self->{_relationship_data}{$rel})
-    );
-    $_->update for grep defined, $self->$rel;
+
+  if (@cascade) {
+    my $guard = $source->schema->txn_scope_guard;
+
+    my $ret = $self->next::method(@rest);
+
+    foreach my $rel (@cascade) {
+      next if (
+        $rels{$rel}{attrs}{accessor}
+          &&
+        $rels{$rel}{attrs}{accessor} eq 'single'
+          &&
+        !exists($self->{_relationship_data}{$rel})
+      );
+      $_->update for grep defined, $self->$rel;
+    }
+
+    $guard->commit;
+    return $ret;
   }
-  return $ret;
+
+  $self->next::method(@rest);
 }
 
 1;
index 3c3895c..4247459 100644 (file)
@@ -3259,23 +3259,27 @@ names:
     select => [
       'name',
       { count => 'employeeid' },
-      { sum => 'salary' }
+      { max => { length => 'name' }, -as => 'longest_name' }
     ]
   });
 
-When you use function/stored procedure names and do not supply an C<as>
-attribute, the column names returned are storage-dependent. E.g. MySQL would
-return a column named C<count(employeeid)> in the above example.
+  # Equivalent SQL
+  SELECT name, COUNT( employeeid ), MAX( LENGTH( name ) ) AS longest_name FROM employee
 
-B<NOTE:> You will almost always need a corresponding 'as' entry when you use
-'select'.
+B<NOTE:> You will almost always need a corresponding L</as> attribute when you
+use L</select>, to instruct DBIx::Class how to store the result of the column.
+Also note that the L</as> attribute has nothing to do with the SQL-side 'AS'
+identifier aliasing. You can however alias a function, so you can use it in
+e.g. an C<ORDER BY> clause. This is done via the C<-as> B<select function
+attribute> supplied as shown in the example above.
 
 =head2 +select
 
 =over 4
 
 Indicates additional columns to be selected from storage.  Works the same as
-L</select> but adds columns to the selection.
+L</select> but adds columns to the default selection, instead of specifying
+an explicit list.
 
 =back
 
@@ -3295,25 +3299,26 @@ Indicates additional column names for those added via L</+select>. See L</as>.
 
 =back
 
-Indicates column names for object inflation. That is, C<as>
-indicates the name that the column can be accessed as via the
-C<get_column> method (or via the object accessor, B<if one already
-exists>).  It has nothing to do with the SQL code C<SELECT foo AS bar>.
-
-The C<as> attribute is used in conjunction with C<select>,
-usually when C<select> contains one or more function or stored
-procedure names:
+Indicates column names for object inflation. That is L</as> indicates the
+slot name in which the column value will be stored within the
+L<Row|DBIx::Class::Row> object. The value will then be accessible via this
+identifier by the C<get_column> method (or via the object accessor B<if one
+with the same name already exists>) as shown below. The L</as> attribute has
+B<nothing to do> with the SQL-side C<AS>. See L</select> for details.
 
   $rs = $schema->resultset('Employee')->search(undef, {
     select => [
       'name',
-      { count => 'employeeid' }
+      { count => 'employeeid' },
+      { max => { length => 'name' }, -as => 'longest_name' }
     ],
-    as => ['name', 'employee_count'],
+    as => [qw/
+      name
+      employee_count
+      max_name_length
+    /],
   });
 
-  my $employee = $rs->first(); # get the first Employee
-
 If the object against which the search is performed already has an accessor
 matching a column name specified in C<as>, the value can be retrieved using
 the accessor as normal:
@@ -3328,16 +3333,6 @@ use C<get_column> instead:
 You can create your own accessors if required - see
 L<DBIx::Class::Manual::Cookbook> for details.
 
-Please note: This will NOT insert an C<AS employee_count> into the SQL
-statement produced, it is used for internal access only. Thus
-attempting to use the accessor in an C<order_by> clause or similar
-will fail miserably.
-
-To get around this limitation, you can supply literal SQL to your
-C<select> attribute that contains the C<AS alias> text, e.g.
-
-  select => [\'myfield AS alias']
-
 =head2 join
 
 =over 4
index a06b6cb..8676ff6 100644 (file)
@@ -5,26 +5,20 @@ use Test::More;
 use lib 't/lib';
 use DBICTest;
 
-my @MODULES = (
-  'Test::NoTabs 0.9',
-);
-
-plan skip_all => 'Does not work with done_testing, temp disabled';
-
 # Don't run tests for installs
 unless ( DBICTest::AuthorCheck->is_author || $ENV{AUTOMATED_TESTING} || $ENV{RELEASE_TESTING} ) {
   plan( skip_all => "Author tests not required for installation" );
 }
-# Load the testing modules
-foreach my $MODULE ( @MODULES ) {
-  eval "use $MODULE";
-  if ( $@ ) {
-    $ENV{RELEASE_TESTING}
-    ? die( "Failed to load required release-testing module $MODULE" )
-    : plan( skip_all => "$MODULE not available for testing" );
-  }
+
+require DBIx::Class;
+unless ( DBIx::Class::Optional::Dependencies->req_ok_for ('test_notabs') ) {
+  my $missing = DBIx::Class::Optional::Dependencies->req_missing_for ('test_notabs');
+  $ENV{RELEASE_TESTING} || DBICTest::AuthorCheck->is_author
+    ? die ("Failed to load release-testing module requirements: $missing")
+    : plan skip_all => "Test needs: $missing"
 }
 
-all_perl_files_ok(qw/t lib script maint/);
+Test::NoTabs::all_perl_files_ok(qw/t lib script maint/);
 
-done_testing;
+# FIXME - need to fix Test::NoTabs
+#done_testing;
index 36a690e..85301ef 100644 (file)
--- a/t/07eol.t
+++ b/t/07eol.t
@@ -5,29 +5,25 @@ use Test::More;
 use lib 't/lib';
 use DBICTest;
 
-my @MODULES = (
-  'Test::EOL 0.6',
-);
-
-plan skip_all => 'Does not work with done_testing, temp disabled';
-
 # Don't run tests for installs
 unless ( DBICTest::AuthorCheck->is_author || $ENV{AUTOMATED_TESTING} || $ENV{RELEASE_TESTING} ) {
   plan( skip_all => "Author tests not required for installation" );
 }
-# Load the testing modules
-foreach my $MODULE ( @MODULES ) {
-  eval "use $MODULE";
-  if ( $@ ) {
-    $ENV{RELEASE_TESTING}
-    ? die( "Failed to load required release-testing module $MODULE" )
-    : plan( skip_all => "$MODULE not available for testing" );
-  }
+
+plan skip_all => 'Test::EOL very broken';
+
+require DBIx::Class;
+unless ( DBIx::Class::Optional::Dependencies->req_ok_for ('test_eol') ) {
+  my $missing = DBIx::Class::Optional::Dependencies->req_missing_for ('test_eol');
+  $ENV{RELEASE_TESTING} || DBICTest::AuthorCheck->is_author
+    ? die ("Failed to load release-testing module requirements: $missing")
+    : plan skip_all => "Test needs: $missing"
 }
 
 TODO: {
   local $TODO = 'Do not fix those yet - we have way too many branches out there, merging will be hell';
-  all_perl_files_ok({ trailing_whitespace => 1}, qw/t lib script maint/);
+  Test::EOL::all_perl_files_ok({ trailing_whitespace => 1}, qw/t lib script maint/);
 }
 
-done_testing;
+# FIXME - need to fix Test::EOL
+#done_testing;
index 2a592e1..a13c651 100644 (file)
@@ -150,10 +150,9 @@ my $fail_code = sub {
   no warnings 'redefine';
   no strict 'refs';
 
-  # die in rollback, but maintain sanity for further tests ...
+  # die in rollback
   local *{"DBIx::Class::Storage::DBI::SQLite::txn_rollback"} = sub{
     my $storage = shift;
-    $storage->{transaction_depth}--;
     die 'FAILED';
   };
 
@@ -180,6 +179,9 @@ my $fail_code = sub {
   $schema->storage->_dbh->rollback;
 }
 
+# reset schema object (the txn_rollback meddling screws it up)
+$schema = DBICTest->init_schema();
+
 # Test nested failed txn_do()
 {
   is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
index 9ca9c2e..63651f6 100644 (file)
@@ -64,36 +64,36 @@ $employee = $employees->search({group_id=>4})->first;
 $employee->group_id(1);
 $employee->update;
 ok(
-       check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})),
-       "overloaded update 3"
+  check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})),
+  "overloaded update 3"
 );
 $employee = $employees->search({group_id=>4})->first;
 $employee->update({group_id=>2});
 ok(
-       check_rs($employees->search_rs({group_id=>2})) && check_rs($employees->search_rs({group_id=>4})),
-       "overloaded update 4"
+  check_rs($employees->search_rs({group_id=>2})) && check_rs($employees->search_rs({group_id=>4})),
+  "overloaded update 4"
 );
 $employee = $employees->search({group_id=>4})->first;
 $employee->group_id(1);
 $employee->position(3);
 $employee->update;
 ok(
-       check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})),
-       "overloaded update 5"
+  check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})),
+  "overloaded update 5"
 );
 $employee = $employees->search({group_id=>4})->first;
 $employee->group_id(2);
 $employee->position(undef);
 $employee->update;
 ok(
-       check_rs($employees->search_rs({group_id=>2})) && check_rs($employees->search_rs({group_id=>4})),
-       "overloaded update 6"
+  check_rs($employees->search_rs({group_id=>2})) && check_rs($employees->search_rs({group_id=>4})),
+  "overloaded update 6"
 );
 $employee = $employees->search({group_id=>4})->first;
 $employee->update({group_id=>1,position=>undef});
 ok(
-       check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})),
-       "overloaded update 7"
+  check_rs($employees->search_rs({group_id=>1})) && check_rs($employees->search_rs({group_id=>4})),
+  "overloaded update 7"
 );
 
 # multicol tests begin here
@@ -154,7 +154,7 @@ $employees = $employees->search(undef,{order_by=>[qw/group_id_2 group_id_3 posit
 $employee = $employees->search({group_id_2=>4, group_id_3=>1})->first;
 $employee->group_id_2(1);
 $employee->update;
-ok( 
+ok(
     check_rs($employees->search_rs({group_id_2=>4, group_id_3=>1}))
     && check_rs($employees->search_rs({group_id_2=>1, group_id_3=>1})), 
     "overloaded multicol update 1" 
index 9bf015a..30c2bca 100644 (file)
@@ -37,13 +37,7 @@ __PACKAGE__->add_columns(
 __PACKAGE__->set_primary_key('employee_id');
 __PACKAGE__->position_column('position');
 
-#__PACKAGE__->add_unique_constraint(position_group => [ qw/position group_id/ ]);
-
-__PACKAGE__->mk_classdata('field_name_for', {
-    employee_id => 'primary key',
-    position    => 'list position',
-    group_id    => 'collection column',
-    name        => 'employee name',
-});
+# Do not add unique constraints here - different groups are used throughout
+# the ordered tests
 
 1;