Re: Savepoint or subtransaction support

Markus Schiltknecht <[email protected]> Fri, 06 Oct 2006 12:56:13 +0200
Newsgroups gmane.comp.python.db.pypgsql.user
Message-ID <[email protected]>
This is a MIME-formatted message.  If you see this text it means that your
E-mail software does not support MIME-formatted messages.

--=_molabola.bugaboo.mu-24216-1160132174-0001-2
Content-Type: text/plain; charset=iso-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Hi,

Our patches were very similar, I like the single rollback() method with 
added support for savepoints. Adding a rollback_savepoint method (as I 
did) is not that elegant.

I've modified my patch to support multiple savepoints. I'm now using the 
same methods as the original patch (i.e. savepoint, release and rollback)

The original patch from [1] did release a savepoint if it already 
existed. I think that's dangerous. In fact, PostgreSQL even allows to 
define multiple savepoints with the very same name.

Also, if you rollback to a savepoint, the savepoint is not released. So 
you can rollback to a savepoint several times. This also allows you to 
call rollback() and then release() on the same savepoint.

If you define multiple savepoints A, B, C (in that order), rolling back 
to savepoint A obviously releases the savepoints B and C. My patch takes 
that into accont. Please note that savepoint A still remains and does 
not get released when rolling back to it.

(I've tried to upload the page to the 'patches' page on SourceForge, but 
didn't succeed, I've only added a useless comment, sorry. Please bear 
with a SF-first-timer.)

Regards

Markus

[1]: the original patch:
https://sourceforge.net/tracker/?func=detail&atid=316528&aid=1511984&group_id=16528

Markus Schiltknecht wrote:
> Hi,
> 
> as I really urgently need savepoints, I have written a small patch to 
> add support for one savepoint. Only to find out that I should have 
> looked in the SF 'patches' page where someone else did exactly the same...
> 
> I'm comparing our patches just now.
> 
> Regards
> 
> Markus


--=_molabola.bugaboo.mu-24216-1160132174-0001-2
Content-Type: text/plain; name=diff; charset=iso-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="diff"

Index: pyPgSQL/PgSQL.py
===================================================================
RCS file: /cvsroot/pypgsql/pypgsql/pyPgSQL/PgSQL.py,v
retrieving revision 1.50
diff -c -r1.50 PgSQL.py
*** pyPgSQL/PgSQL.py	1 Jun 2006 14:42:51 -0000	1.50
--- pyPgSQL/PgSQL.py	6 Oct 2006 10:37:52 -0000
***************
*** 2368,2373 ****
--- 2368,2374 ----
  	self.__dict__["TransactionLevel"] = ""
  	self.__dict__["notices"] = self.conn.notices
  	self.__dict__["inTransaction"] = 0
+ 	self.__dict__["savepoints"] = None
  	self.__dict__["version"] = self.conn.version
  	self.__dict__["_isOpen"] = 1
  	self.__dict__["_cache"] = TypeCache(self)
***************
*** 2515,2520 ****
--- 2516,2522 ----
              if len(self.notices) != _nl:
                  raise Warning, self.notices.pop()
          self.__dict__["inTransaction"] = 1
+         self.__dict__["savepoints"] = []
  
          
      def close(self):
***************
*** 2542,2547 ****
--- 2544,2550 ----
  	self.__dict__["conn"] = None
  	self.__dict__["cursors"] = None
  	self.__dict__["inTransaction"] = 0
+ 	self.__dict__["savepoints"] = None
  	self.__dict__["TransactionLevel"] = None
  	self.__dict__["version"] = None
  	self.__dict__["notices"] = None
***************
*** 2559,2564 ****
--- 2562,2568 ----
  
  	if self.__closeCursors():
  	    self.__dict__["inTransaction"] = 0
+ 	    self.__dict__["savepoints"] = None
  	    _nl = len(self.conn.notices)
  	    res = self.conn.query("COMMIT WORK")
  	    if len(self.notices) != _nl:
***************
*** 2566,2575 ****
  	    if res.resultStatus != COMMAND_OK:
  		raise InternalError, "Commit failed - reason unknown."
  
!     def rollback(self):
  	"""
      rollback()
! 	Rollback to the start of any pending transactions.\n"""
  
  	if not self._isOpen:
  	    raise InterfaceError, "Rollback failed - Connection is not open."
--- 2570,2605 ----
  	    if res.resultStatus != COMMAND_OK:
  		raise InternalError, "Commit failed - reason unknown."
  
!     def savepoint(self, name):
! 	"""
!     savepoint()
! 	Set a savepoint in the current connection, to which the connection
! 	gets rolled back to on error or on request.\n"""
! 
! 	if not self._isOpen:
! 	    raise InterfaceError, "Savepoint failed - Connection is not open."
! 
! 	if self.autocommit:
! 	    raise InterfaceError, "Savepoint failed - autocommit is on."
! 
! 	if not self.inTransaction:
! 	    raise InterfaceError, "Savepoint failed - not in a transaction."
! 
! 	_nl = len(self.conn.notices)
! 	res = self.conn.query("SAVEPOINT %s" % name)
! 	if len(self.notices) != _nl:
! 	    raise Warning, self.notices.pop()
! 	if res.resultStatus != COMMAND_OK:
! 	    raise InternalError, \
! 		    "Savepoint failed - %s" % res.resultErrorMessage
! 
! 	self.__dict__["savepoints"].append(name)
! 
!     def rollback(self, toSavepoint=None):
  	"""
      rollback()
! 	Rollback to the start of any pending transactions on to a
! 	savepoint.\n"""
  
  	if not self._isOpen:
  	    raise InterfaceError, "Rollback failed - Connection is not open."
***************
*** 2578,2592 ****
  	    raise InterfaceError, "Rollback failed - autocommit is on."
  
  	if self.__closeCursors():
- 	    self.__dict__["inTransaction"] = 0
  	    _nl = len(self.conn.notices)
! 	    res = self.conn.query("ROLLBACK WORK")
  	    if len(self.notices) != _nl:
  		raise Warning, self.notices.pop()
  	    if res.resultStatus != COMMAND_OK:
  		raise InternalError, \
  		      	"Rollback failed - %s" % res.resultErrorMessage
  
      def cursor(self, name=None, isRefCursor=PG_False):
  	"""
      cursor([name])
--- 2608,2660 ----
  	    raise InterfaceError, "Rollback failed - autocommit is on."
  
  	if self.__closeCursors():
  	    _nl = len(self.conn.notices)
! 	    if toSavepoint:
! 		try:
! 		    idx = self.savepoints.index(toSavepoint)
! 		    self.__dict__["savepoints"] = self.savepoints[:idx+1]
! 		except:
! 		    raise InterfaceError, "Rollback failed - no such savepoint."
! 
! 		res = self.conn.query("ROLLBACK TO SAVEPOINT %s" % toSavepoint)
! 
! 	    else:
! 		self.__dict__["inTransaction"] = 0
! 		res = self.conn.query("ROLLBACK WORK")
! 
  	    if len(self.notices) != _nl:
  		raise Warning, self.notices.pop()
  	    if res.resultStatus != COMMAND_OK:
  		raise InternalError, \
  		      	"Rollback failed - %s" % res.resultErrorMessage
  
+     def release(self, name=None):
+ 	if not self._isOpen:
+ 	    raise InterfaceError, "Release failed - Connection is not open."
+ 
+ 	if self.autocommit:
+ 	    raise InterfaceError, "Release failed - autocommit is on."
+ 
+ 	if not self.inTransaction:
+ 	    raise InterfaceError, "Release failed - not in a transaction."
+ 
+ 	if not name:
+ 	    name = self.savepoints[-1]
+ 	else:
+ 	    try:
+ 		idx = self.savepoints.index(name)
+ 		self.__dict__["savepoints"] = self.savepoints[:idx+1]
+ 	    except:
+ 		raise InterfaceError, "Rollback failed - no savepoint named %s." % name
+ 
+ 	_nl = len(self.conn.notices)
+ 	res = self.conn.query("RELEASE SAVEPOINT %s" % name)
+ 	if len(self.notices) != _nl:
+ 	    raise Warning, self.notices.pop()
+ 	if res.resultStatus != COMMAND_OK:
+ 	    raise InternalError, \
+ 		    "Release savepoint failed - %s" % res.resultErrorMessage
+ 
      def cursor(self, name=None, isRefCursor=PG_False):
  	"""
      cursor([name])
***************
*** 2982,2989 ****
  	    # Uh-oh.  A fatal error occurred.  This means the current trans-
  	    # action has been aborted.  Try to recover to a sane state.
  	    if self.conn.inTransaction:
! 		self.conn.conn.query('END WORK')
! 		self.conn.__dict__["inTransaction"] = 0
  		self.conn._Connection__closeCursors()
  	    raise OperationalError, msg
  	except InternalError, msg:
--- 3050,3060 ----
  	    # Uh-oh.  A fatal error occurred.  This means the current trans-
  	    # action has been aborted.  Try to recover to a sane state.
  	    if self.conn.inTransaction:
! 		if len(self.conn.savepoints) > 0:
! 		    self.conn.conn.query('ROLLBACK TO SAVEPOINT %s' % self.conn.savepoints[-1])
! 		else:
! 		    self.conn.conn.query('END WORK')
! 		    self.conn.__dict__["inTransaction"] = 0
  		self.conn._Connection__closeCursors()
  	    raise OperationalError, msg
  	except InternalError, msg:
***************
*** 3103,3113 ****
  	    # action has been aborted.  Try to recover to a sane state.
  	    if self.conn.inTransaction:
  		_n = len(self.conn.notices)
! 		self.conn.conn.query('ROLLBACK WORK')
  		if len(self.conn.notices) != _n:
  		    raise Warning, self.conn.notices.pop()
- 		self.conn.__dict__["inTransaction"] = 0
- 		self.conn._Connection__closeCursors()
  	    raise OperationalError, msg
  	except InternalError, msg:
  	    # An internal error occured.  Try to get to a sane state.
--- 3174,3187 ----
  	    # action has been aborted.  Try to recover to a sane state.
  	    if self.conn.inTransaction:
  		_n = len(self.conn.notices)
! 		if len(self.conn.savepoints) > 0:
! 		    self.conn.conn.query('ROLLBACK TO SAVEPOINT %s' % self.conn.savepoints[-1])
! 		else:
! 		    self.conn.conn.query('ROLLBACK WORK')
! 		    self.conn.__dict__["inTransaction"] = 0
! 		    self.conn._Connection__closeCursors()
  		if len(self.conn.notices) != _n:
  		    raise Warning, self.conn.notices.pop()
  	    raise OperationalError, msg
  	except InternalError, msg:
  	    # An internal error occured.  Try to get to a sane state.

--=_molabola.bugaboo.mu-24216-1160132174-0001-2
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
--=_molabola.bugaboo.mu-24216-1160132174-0001-2
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
Pypgsql-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/pypgsql-users

--=_molabola.bugaboo.mu-24216-1160132174-0001-2--