improve with_deferred_fk_checks for Oracle, add tests
Rafael Kitover [Mon, 22 Feb 2010 20:48:43 +0000 (20:48 +0000)]
Makefile.PL
lib/DBIx/Class/Storage/DBI/Oracle/Generic.pm
t/73oracle.t

index d98c782..fd920f9 100644 (file)
@@ -49,6 +49,8 @@ my $runtime_requires = {
   'SQL::Abstract::Limit'     => '0.13',
   'Sub::Name'                => '0.04',
   'Data::Dumper::Concise'    => '1.000',
+  'Scope::Guard'             => '0.03',
+  'Context::Preserve'        => '0.01',
 };
 
 # this is so we can order requires alphabetically
index 5973319..448c203 100644 (file)
@@ -2,6 +2,8 @@ package DBIx::Class::Storage::DBI::Oracle::Generic;
 
 use strict;
 use warnings;
+use Scope::Guard ();
+use Context::Preserve ();
 
 =head1 NAME
 
@@ -334,15 +336,25 @@ Runs a coderef between:
   ...
   alter session set constraints = immediate
 
-to defer FK checks.
+to defer foreign key checks.
+
+Constraints must be declared C<DEFERRABLE> for this to work.
 
 =cut
 
 sub with_deferred_fk_checks {
   my ($self, $sub) = @_;
+
+  my $txn_scope_guard = $self->txn_scope_guard;
+
   $self->_do_query('alter session set constraints = deferred');
-  $sub->();
-  $self->_do_query('alter session set constraints = immediate');
+  
+  my $sg = Scope::Guard->new(sub {
+    $self->_do_query('alter session set constraints = immediate');
+  });
+
+  return Context::Preserve::preserve_context(sub { $sub->() },
+    after => sub { $txn_scope_guard->commit });
 }
 
 =head1 AUTHOR
index 50c519f..0aa3ee7 100644 (file)
@@ -53,22 +53,26 @@ eval {
   $dbh->do("DROP SEQUENCE nonpkid_seq");
   $dbh->do("DROP TABLE artist");
   $dbh->do("DROP TABLE sequence_test");
-  $dbh->do("DROP TABLE cd");
   $dbh->do("DROP TABLE track");
+  $dbh->do("DROP TABLE cd");
 };
 $dbh->do("CREATE SEQUENCE artist_seq START WITH 1 MAXVALUE 999999 MINVALUE 0");
 $dbh->do("CREATE SEQUENCE cd_seq START WITH 1 MAXVALUE 999999 MINVALUE 0");
 $dbh->do("CREATE SEQUENCE pkid1_seq START WITH 1 MAXVALUE 999999 MINVALUE 0");
 $dbh->do("CREATE SEQUENCE pkid2_seq START WITH 10 MAXVALUE 999999 MINVALUE 0");
 $dbh->do("CREATE SEQUENCE nonpkid_seq START WITH 20 MAXVALUE 999999 MINVALUE 0");
+
 $dbh->do("CREATE TABLE artist (artistid NUMBER(12), name VARCHAR(255), rank NUMBER(38), charfield VARCHAR2(10))");
+$dbh->do("ALTER TABLE artist ADD (CONSTRAINT artist_pk PRIMARY KEY (artistid))");
+
 $dbh->do("CREATE TABLE sequence_test (pkid1 NUMBER(12), pkid2 NUMBER(12), nonpkid NUMBER(12), name VARCHAR(255))");
-$dbh->do("CREATE TABLE cd (cdid NUMBER(12), artist NUMBER(12), title VARCHAR(255), year VARCHAR(4), genreid NUMBER(12), single_track NUMBER(12))");
-$dbh->do("CREATE TABLE track (trackid NUMBER(12), cd NUMBER(12), position NUMBER(12), title VARCHAR(255), last_updated_on DATE, last_updated_at DATE, small_dt DATE)");
+$dbh->do("ALTER TABLE sequence_test ADD (CONSTRAINT sequence_test_constraint PRIMARY KEY (pkid1, pkid2))");
 
-$dbh->do("ALTER TABLE artist ADD (CONSTRAINT artist_pk PRIMARY KEY (artistid))");
+$dbh->do("CREATE TABLE cd (cdid NUMBER(12), artist NUMBER(12), title VARCHAR(255), year VARCHAR(4), genreid NUMBER(12), single_track NUMBER(12))");
 $dbh->do("ALTER TABLE cd ADD (CONSTRAINT cd_pk PRIMARY KEY (cdid))");
-$dbh->do("ALTER TABLE sequence_test ADD (CONSTRAINT sequence_test_constraint PRIMARY KEY (pkid1, pkid2))");
+
+$dbh->do("CREATE TABLE track (trackid NUMBER(12), cd NUMBER(12) REFERENCES cd(cdid) DEFERRABLE, position NUMBER(12), title VARCHAR(255), last_updated_on DATE, last_updated_at DATE, small_dt DATE)");
+
 $dbh->do(qq{
   CREATE OR REPLACE TRIGGER artist_insert_trg
   BEFORE INSERT ON artist
@@ -218,6 +222,27 @@ is( $it->next, undef, "next past end of resultset ok" );
   is( scalar @results, 1, "Group by with limit OK" );
 }
 
+# test with_deferred_fk_checks
+lives_ok {
+  $schema->storage->with_deferred_fk_checks(sub {
+    $schema->resultset('Track')->create({
+      trackid => 999, cd => 999, position => 1, title => 'deferred FK track'
+    });
+    $schema->resultset('CD')->create({
+      artist => 1, cdid => 999, year => '2003', title => 'deferred FK cd'
+    });
+  });
+} 'with_deferred_fk_checks code survived';
+
+is eval { $schema->resultset('Track')->find(999)->title }, 'deferred FK track',
+   'code in with_deferred_fk_checks worked'; 
+
+throws_ok {
+  $schema->resultset('Track')->create({
+    trackid => 1, cd => 9999, position => 1, title => 'Track1'
+  });
+} qr/constraint/i, 'with_deferred_fk_checks is off';
+
 # test auto increment using sequences WITHOUT triggers
 for (1..5) {
     my $st = $schema->resultset('SequenceTest')->create({ name => 'foo' });
@@ -271,8 +296,8 @@ END {
         $dbh->do("DROP SEQUENCE nonpkid_seq");
         $dbh->do("DROP TABLE artist");
         $dbh->do("DROP TABLE sequence_test");
-        $dbh->do("DROP TABLE cd");
         $dbh->do("DROP TABLE track");
+        $dbh->do("DROP TABLE cd");
         $dbh->do("DROP TABLE bindtype_test");
     }
 }