From e8aa14844b36259100178a920e85c2d192d28a42 Mon Sep 17 00:00:00 2001
From: Sebastien Robin <seb@nexedi.com>
Date: Fri, 14 May 2004 10:19:31 +0000
Subject: [PATCH] added patch for zope Transaction so that it can handle
 _commit_prepare

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@878 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5Type/ZopePatch.py | 160 ++++++++++++++++++++++++++++++++++
 1 file changed, 160 insertions(+)

diff --git a/product/ERP5Type/ZopePatch.py b/product/ERP5Type/ZopePatch.py
index f0bca2aedd..76934ad94b 100755
--- a/product/ERP5Type/ZopePatch.py
+++ b/product/ERP5Type/ZopePatch.py
@@ -614,3 +614,163 @@ class ERP5TransitionDefinition (TransitionDefinition):
           lambda k: self.getWorkflow().transitions[k].trigger_type == TRIGGER_WORKFLOW_METHOD, self.getWorkflow().transitions.keys())
 
 TransitionDefinition.getAvailableScriptIds = ERP5TransitionDefinition.getAvailableScriptIds
+
+##############################################################################
+# Adding commit_prepare to the zodb transaction
+from ZODB import Transaction
+
+#class ERP5Transaction(Transaction):
+
+hosed = Transaction.hosed
+free_transaction = Transaction.free_transaction
+jar_cmp = Transaction.jar_cmp
+
+def commit(self, subtransaction=None):
+    """Finalize the transaction."""
+    objects = self._objects
+
+    subjars = []
+    if subtransaction:
+        if self._sub is None:
+            # Must store state across multiple subtransactions
+            # so that the final commit can commit all subjars.
+            self._sub = {}
+    else:
+        if self._sub is not None:
+            # This commit is for a top-level transaction that
+            # has previously committed subtransactions.  Do
+            # one last subtransaction commit to clear out the
+            # current objects, then commit all the subjars.
+            if objects:
+                self.commit(1)
+                objects = []
+            subjars = self._sub.values()
+            subjars.sort(jar_cmp)
+            self._sub = None
+
+            # If there were any non-subtransaction-aware jars
+            # involved in earlier subtransaction commits, we need
+            # to add them to the list of jars to commit.
+            if self._non_st_objects is not None:
+                objects.extend(self._non_st_objects)
+                self._non_st_objects = None
+
+    if (objects or subjars) and hosed:
+        # Something really bad happened and we don't
+        # trust the system state.
+        raise POSException.TransactionError, hosed_msg
+
+    # It's important that:
+    #
+    # - Every object in self._objects is either committed or
+    #   aborted.
+    #
+    # - For each object that is committed we call tpc_begin on
+    #   it's jar at least once
+    #
+    # - For every jar for which we've called tpc_begin on, we
+    #   either call tpc_abort or tpc_finish. It is OK to call
+    #   these multiple times, as the storage is required to ignore
+    #   these calls if tpc_begin has not been called.
+    #
+    # - That we call tpc_begin() in a globally consistent order,
+    #   so that concurrent transactions involving multiple storages
+    #   do not deadlock.
+    try:
+        ncommitted = 0
+        # Do prepare until number of jars is stable - this could 
+        # create infinite loop
+        jars_len = -1
+        jars = self._get_jars(objects, subtransaction)            
+        while len(jars) != jars_len:             
+          jars_len = len(jars)
+          self._commit_prepare(jars, subjars, subtransaction)
+          jars = self._get_jars(objects, subtransaction)                        
+        try:
+            # If not subtransaction, then jars will be modified.
+            self._commit_begin(jars, subjars, subtransaction)
+            ncommitted += self._commit_objects(objects)
+            if not subtransaction:
+                # Unless this is a really old jar that doesn't
+                # implement tpc_vote(), it must raise an exception
+                # if it can't commit the transaction.
+                for jar in jars:
+                    try:
+                        vote = jar.tpc_vote
+                    except AttributeError:
+                        pass
+                    else:
+                        vote(self)
+
+            # Handle multiple jars separately.  If there are
+            # multiple jars and one fails during the finish, we
+            # mark this transaction manager as hosed.
+            if len(jars) == 1:
+                self._finish_one(jars[0])
+            else:
+                self._finish_many(jars)
+        except:
+            # Ugh, we got an got an error during commit, so we
+            # have to clean up.  First save the original exception
+            # in case the cleanup process causes another
+            # exception.
+            error = sys.exc_info()
+            try:
+                self._commit_error(objects, ncommitted, jars, subjars)
+            except:
+                LOG('ZODB', ERROR,
+                    "A storage error occured during transaction "
+                    "abort.  This shouldn't happen.",
+                    error=error)
+            raise error[0], error[1], error[2]
+    finally:
+        del objects[:] # clear registered
+        if not subtransaction and self._id is not None:
+            free_transaction()
+
+def _commit_prepare(self, jars, subjars, subtransaction):
+    if subtransaction:
+        assert not subjars
+        for jar in jars:
+            try:
+                jar.tpc_prepare(self, subtransaction)
+            except TypeError:
+                # Assume that TypeError means that tpc_begin() only
+                # takes one argument, and that the jar doesn't
+                # support subtransactions.
+                jar.tpc_prepare(self)
+            except AttributeError:
+                # Assume that KeyError means that tpc_prepare
+                # not available
+                pass                    
+    else:
+        # Merge in all the jars used by one of the subtransactions.
+
+        # When the top-level subtransaction commits, the tm must
+        # call commit_sub() for each jar involved in one of the
+        # subtransactions.  The commit_sub() method should call
+        # tpc_begin() on the storage object.
+
+        # It must also call tpc_begin() on jars that were used in
+        # a subtransaction but don't support subtransactions.
+
+        # These operations must be performed on the jars in order.
+
+        # Modify jars inplace to include the subjars, too.
+        jars += subjars
+        jars.sort(jar_cmp)
+        # assume that subjars is small, so that it's cheaper to test
+        # whether jar in subjars than to make a dict and do has_key.
+        for jar in jars:
+            #if jar in subjars:
+            #  pass
+            #else:
+            try:
+                jar.tpc_prepare(self)
+            except AttributeError:
+                # Assume that KeyError means that tpc_prepare
+                # not available
+                pass                    
+
+Transaction.Transaction.commit = commit
+Transaction.Transaction._commit_prepare = _commit_prepare
-- 
2.30.9