Hi!

A few weeks ago I prepared a doc patch regarding nested transactions, 
but never submitted the (hopefully) final version. Here it is now...

If the patch is accepted, do you need any further stuff (for licensing 
issues, etc)?

Thanks...

-- 
#!/usr/bin/perl                              http://domm.plix.at
for(ref bless{},just'another'perl'hacker){s-:+-$"-g&&print$_.$/}
Index: lib/DBIx/Class/Manual/Cookbook.pod
===================================================================
--- lib/DBIx/Class/Manual/Cookbook.pod	(revision 7340)
+++ lib/DBIx/Class/Manual/Cookbook.pod	(working copy)
@@ -1232,10 +1232,101 @@
 Nested transactions will work as expected. That is, only the outermost
 transaction will actually issue a commit to the $dbh, and a rollback
 at any level of any transaction will cause the entire nested
-transaction to fail. Support for savepoints and for true nested
-transactions (for databases that support them) will hopefully be added
-in the future.
+transaction to fail.
+ 
+=head2 Savepoints and nested transactions
 
+Savepoints and true nested transactions might be supported, depending on the
+database backend used. To enable savepoints / nested transactions, connect with
+C<< auto_savepoint = 1 >>.
+
+Here is an example for true nested transaction. In the example, we start a big
+job which will create several things. But creating things is fragile and might
+fail. If we fail creating a thing, depending on the type of failure, we want to
+abort the whole job, or only skip the failed thing.
+
+  my $schema = MySchema->connect("dbi:Pg:dbname=my_db");
+
+  # Start a transaction. Every database change from here on will only be 
+  # commited into the database if the eval block succeeds.
+  eval {
+    $schema->txn_do(sub {
+      # SQL: BEGIN WORK;
+      
+      my $job = $schema->resultset('Job')->create({ name=> 'big job' });
+      # SQL: INSERT INTO job ( name) VALUES ( 'big job' );
+
+      for (1..10) {
+        
+        # Start a nested transaction, which in fact sets a savepoint.
+        eval {
+          $schema->txn_do(sub {
+            # SQL: SAVEPOINT savepoint_0;
+        
+            my $thing = $schema->resultset('Thing')->create({ job=>$job->id });
+            # SQL: INSERT INTO thing ( job) VALUES ( 1 );
+
+            if (rand > 0.8) {
+      	      # This will generate an error, thus setting $@
+      	  
+      	      $thing->update({force_fail=>'foo'});
+      	      # SQL: UPDATE thing SET force_fail = 'foo'
+      	      #      WHERE ( id = 42 );
+            }
+          });
+        };
+        if ($@) {
+          # SQL: ROLLBACK TO SAVEPOINT savepoint_0;
+          
+          # There was an error while creating a $thing. Depending on the error
+          # we want to abort the whole transaction, or only rollback the
+          # changes related to the creation of this $thing
+
+          # Abort the whole job
+          if ($@ =~ /horrible_problem/) {
+            print "something horrible happend, aborting job!";
+            die $@;                # rethrow error
+          }
+          
+          # Ignore this $thing, report the error, and continue with the
+          # next $thing 
+          print "Cannot create thing: $@";
+        }
+        # There was no error, so save all changes since the last 
+        # savepoint.
+        
+        # SQL: RELEASE SAVEPOINT savepoint_0;
+      }
+    });
+  };
+  if ($@) {
+    # There was an error while handling the $job. Rollback all changes 
+    # since the transaction started, including the already commited 
+    # ('released') savepoints. There will be neither a new $job nor any
+    # $thing entry in the database.
+    
+    # SQL: ROLLBACK;
+
+    print "ERROR: $...@\n";
+  }
+  else {
+    # There was no error while handling the $job. Commit all changes.  
+    # Only now other connections can see the newly created $job and 
+    # @things.
+    
+    # SQL: COMMIT;
+    
+    print "Ok\n";
+  }
+
+In this example it might be hard to see where the rollbacks, releases and
+commits are happening, but it works just the same as for plain L<<txn_do>>: If
+the C<eval>-block around C<txn_do> fails, a rollback is issued. If the C<eval>
+succeeds, the transaction is committed (or the savepoint released).
+
+While you can get more fine-grained controll using C<svp_begin>, C<svp_release>
+and C<svp_rollback>, it is recommended to use C<txn_do>.
+
 =head1 SQL
 
 =head2 Creating Schemas From An Existing Database
_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/[email protected]

Reply via email to