@@ -45,6 +45,7 @@ class DB(TM):
4545 _p_oid = _p_changed = None
4646 _registered = False
4747 _sort_key = '1'
48+ _conn = None
4849
4950 def __init__ (self , dsn , tilevel , typecasts , enc = 'utf-8' ):
5051 self .dsn = dsn
@@ -59,6 +60,20 @@ def __init__(self, dsn, tilevel, typecasts, enc='utf-8'):
5960 self .make_mappings ()
6061
6162 def getconn (self , init = 'ignored' ):
63+ if self ._conn :
64+ # As the TM is a short-lived object, keep a reference to the connection - we really
65+ # expect the pool's getconn() to reliably return the same connection anyway.
66+ # This is needed to differentiate between an initial connection
67+ # where its fine to keep going through the pool and fetching a new one
68+ # versus an existing connection in a running transaction where e.g. a failure
69+ # on the server side has aborted the transaction and we can't e.g. run `select 1`
70+ # any longer but we also need the original connection to clean up correctly within
71+ # Zope's transaction management.
72+ # Theoretically this could be placed in `_register()` but I'm not 100% sure
73+ # someone might be using getconn() without the TM integration thus going around
74+ # `_register()` ...
75+ return self ._conn
76+
6277 _pool = pool .getpool (self .dsn , create = True )
6378
6479 # Loop to support cleaning up potentially all `maxconn` faulty
@@ -74,22 +89,21 @@ def getconn(self, init='ignored'):
7489 except (psycopg2 .InterfaceError , psycopg2 .OperationalError ):
7590 pool .putconn (self .dsn , conn , close = True )
7691
77- if id (conn ) in _pool ._initialized :
78- return conn
92+ if id (conn ) not in _pool ._initialized :
93+ conn .set_session (isolation_level = int (self .tilevel ))
94+ conn .set_client_encoding (self .encoding )
95+ for tc in self .typecasts :
96+ register_type (tc , conn )
97+ _pool ._initialized .add (id (conn ))
7998
80- conn .set_session (isolation_level = int (self .tilevel ))
81- conn .set_client_encoding (self .encoding )
82- for tc in self .typecasts :
83- register_type (tc , conn )
84- _pool ._initialized .add (id (conn ))
99+ self ._conn = conn
85100 return conn
86101
87102 def putconn (self , close = False ):
88- try :
89- conn = pool .getconn (self .dsn , False )
90- except AttributeError :
91- pass
92- pool .putconn (self .dsn , conn , close )
103+ if not self ._conn :
104+ return
105+ pool .putconn (self .dsn , self ._conn , close )
106+ self ._conn = None
93107
94108 def getcursor (self ):
95109 conn = self .getconn ()
0 commit comments