@@ -222,6 +222,11 @@ def from_statement(
222222 with_scroll = False ,
223223 insensitive = True ,
224224 ):
225+ if statement ._input is not None :
226+ if len (parameters ) != len (statement ._input ):
227+ raise TypeError ("statement requires %d parameters, given %d" % (
228+ len (statement ._input ), len (parameters )
229+ ))
225230 c = super ().__new__ (typ )
226231 c .parameters = parameters
227232 c .statement = statement
@@ -1427,7 +1432,7 @@ def __enter__(self):
14271432 self ._restore .append (res )
14281433
14291434 def __exit__ (self , exc , val , tb ):
1430- # If the transaction is open, restore the settings.
1435+ # Iff the transaction is open, restore the settings.
14311436 self ._restored .update (self ._restore [- 1 ])
14321437 del self ._restore [- 1 ]
14331438 if not self .database .xact .failed :
@@ -1608,14 +1613,13 @@ def _execute(self, qstring, adjustment):
16081613 pq .element .Query (self .database .typio ._encode (qstring )[0 ]),
16091614 ))
16101615 self .database ._pq_push (x )
1616+ self .database ._pq_complete ()
16111617
1612- # The operation is going to happen. Adjust the depth accordingly.
16131618 if adjustment < 0 and self ._depth <= - adjustment :
16141619 self .__init__ (self .database )
16151620 self ._depth = 0
16161621 else :
16171622 self ._depth += adjustment
1618- self .database ._pq_complete ()
16191623
16201624 def _start_block_string (mode , isolation ):
16211625 return 'START TRANSACTION' + (
@@ -1705,13 +1709,28 @@ def checkpoint(self, isolation = None, mode = None):
17051709 __enter__ = start
17061710 def __context__ (self ):
17071711 return self
1708- def __exit__ (self , type , value , tb ):
1709- if not self .database .closed :
1710- if type is None :
1711- self .commit ()
1712+
1713+ def __exit__ (self , typ , value , tb ):
1714+ if typ is None :
1715+ # No exception, but in a failed transaction?
1716+ if self .failed is True :
1717+ err = pg_exc .InFailedTransactionError (
1718+ "invalid block exit detected" ,
1719+ source = 'DRIVER' ,
1720+ )
1721+ self .ife_descend (err )
1722+ self .rollback ()
1723+ raise err
17121724 else :
1725+ # Everything is fine.
1726+ self .commit ()
1727+ else :
1728+ # There's an exception, so only rollback if the connection
1729+ # exists. If the rollback() was called here, it would just
1730+ # contribute noise to the error.
1731+ if not self .database .closed :
17131732 self .rollback ()
1714- return type is None
1733+ return typ is None
17151734
17161735 def __call__ (self , gid = None , mode = None , isolation = None ):
17171736 if self ._depth == 0 :
@@ -1901,12 +1920,16 @@ def prepare(self,
19011920 ps = PreparedStatement .from_string (sql_statement_string , self )
19021921 self .ife_descend (ps )
19031922 ps ._init ()
1923+ if self .xact .depth > 0 :
1924+ ps ._fini ()
19041925 return ps
19051926
19061927 def statement_from_id (self , statement_id : str ) -> PreparedStatement :
19071928 ps = PreparedStatement (statement_id , self )
19081929 self .ife_descend (ps )
19091930 ps ._init ()
1931+ if self .xact .depth > 0 :
1932+ ps ._fini ()
19101933 return ps
19111934
19121935 def proc (self , proc_id : (str , int )) -> StoredProcedure :
@@ -1918,6 +1941,8 @@ def cursor_from_id(self, cursor_id : str) -> Cursor:
19181941 c = Cursor (cursor_id , self )
19191942 self .ife_descend (c )
19201943 c ._init ()
1944+ if self .xact .depth > 0 :
1945+ c ._fini ()
19211946 return c
19221947
19231948 def close (self ):
@@ -1930,10 +1955,8 @@ def close(self):
19301955 finally :
19311956 if self .socket is not None :
19321957 self .socket .close ()
1933- self ._clear ()
19341958 self ._reset ()
1935- # the data in the closed connection transaction is
1936- # in utf-8.
1959+ # the data in the closed connection transaction is in utf-8.
19371960 self .typio .set_encoding ('utf-8' )
19381961
19391962 @property
@@ -2059,7 +2082,8 @@ def connect(self, timeout = None):
20592082 self .socket .close ()
20602083 self .socket = None
20612084 self ._reset ()
2062- self ._clear ()
2085+ self ._pq_in_buffer .truncate ()
2086+
20632087 connection_failures .append (
20642088 (dossl , socket_maker , e )
20652089 )
@@ -2429,17 +2453,6 @@ def _pq_pop(self, xact = None):
24292453 # state is Complete, so remove it as the working transaction
24302454 self ._pq_xact = None
24312455
2432- def _clear (self ):
2433- """
2434- [internal] Clear container objects of data.
2435- """
2436- del self ._closestatements [:]
2437- del self ._closeportals [:]
2438-
2439- self ._pq_in_buffer .truncate ()
2440- self .xact .__init__ (self )
2441- self .settings ._clear_cache ()
2442-
24432456 def _reset (self ):
24442457 """
24452458 [internal] Reset state and connection information attributes.
@@ -2575,11 +2588,14 @@ def __init__(self,
25752588 tnkw = {}
25762589 if self .settings :
25772590 s = dict (self .settings )
2578- sp = s .get ('search_path' )
2579- if not isinstance (sp , str ):
2580- s ['search_path' ] = ',' .join (
2581- pg_str .quote_ident (x ) for x in sp
2582- )
2591+ if 'search_path' in self .settings :
2592+ sp = s .get ('search_path' )
2593+ if sp is None :
2594+ self .settings .pop ('search_path' )
2595+ elif not isinstance (sp , str ):
2596+ s ['search_path' ] = ',' .join (
2597+ pg_str .quote_ident (x ) for x in sp
2598+ )
25832599 tnkw .update (s )
25842600
25852601 tnkw ['user' ] = self .user
0 commit comments