1 """SteadyPg - hardened classic PyGreSQL connections.
3 Implements steady connections to a PostgreSQL database
4 using the classic (not DB-API 2 compliant) PyGreSQL API.
6 The connections are transparently reopened when they are
7 closed or the database connection has been lost or when
8 they are used more often than an optional usage limit.
9 Only connections which have been marked as being in a database
10 transaction with a begin() call will not be silently replaced.
12 A typical situation where database connections are lost
13 is when the database server or an intervening firewall is
14 shutdown and restarted for maintenance reasons. In such a
15 case, all database connections would become unusable, even
16 though the database service may be already available again.
18 The "hardened" connections provided by this module will
19 make the database connections immediately available again.
21 This results in a steady PostgreSQL connection that can be used
22 by PooledPg or PersistentPg to create pooled or persistent
23 connections to a PostgreSQL database in a threaded environment
24 such as the application server of "Webware for Python."
25 Note, however, that the connections themselves are not thread-safe.
27 For more information on PostgreSQL, see:
28 https://www.postgresql.org/
29 For more information on PyGreSQL, see:
30 http://www.pygresql.org
31 For more information on Webware for Python, see:
32 https://webwareforpython.github.io/w4py/
37 You can use the class SteadyPgConnection in the same way as you
38 would use the class DB from the classic PyGreSQL API module db.
39 The only difference is that you may specify a usage limit as the
40 first parameter when you open a connection (set it to None
41 if you prefer unlimited usage), and an optional list of commands
42 that may serve to prepare the session as the second parameter,
43 and you can specify whether is is allowed to close the connection
44 (by default this is true). When the connection to the PostgreSQL
45 database is lost or has been used too often, it will be automatically
46 reset, without further notice.
48 from dbutils.steady_pg import SteadyPgConnection
49 db = SteadyPgConnection(10000, ["set datestyle to german"],
50 host=..., dbname=..., user=..., ...)
52 result = db.query('...')
57 Ideas for improvement:
59 * Alternatively to the maximum number of uses,
60 implement a maximum time to live for connections.
61 * Optionally log usage and loss of connection.
64 Copyright, credits and license:
66 * Contributed as supplement for Webware for Python and PyGreSQL
67 by Christoph Zwerschke in September 2005
69 Licensed under the MIT license.
72 from pg
import DB
as PgConnection
74 from .
import __version__
83 """General SteadyPg error."""
87 """Database connection is invalid."""
91 """Class representing steady connections to a PostgreSQL database.
93 Underlying the connection is a classic PyGreSQL pg API database
94 connection which is reset if the connection is lost or used too often.
95 Thus the resulting connection is steadier ("tough and self-healing").
97 If you want the connection to be persistent in a threaded environment,
98 then you should not deal with this class directly, but use either the
99 PooledPg module or the PersistentPg module to get the connections.
102 version = __version__
105 self, maxusage=None, setsession=None, closeable=True,
107 """Create a "tough" PostgreSQL connection.
109 maxusage: maximum usage limit for the underlying PyGreSQL connection
110 (number of uses, 0 or None means unlimited usage)
111 When this limit is reached, the connection is automatically reset.
112 setsession: optional list of SQL commands that may serve to prepare
113 the session, e.g. ["set datestyle to ...", "set time zone ..."]
114 closeable: if this is set to false, then closing the connection will
115 be silently ignored, but by default the connection can be closed
116 args, kwargs: the parameters that shall be used to establish
117 the PostgreSQL connections with PyGreSQL using pg.DB()
125 if not isinstance(maxusage, baseint):
126 raise TypeError(
"'maxusage' must be an integer value.")
130 self.
_con = PgConnection(*args, **kwargs)
137 """Enter the runtime context. This will start a transaction."""
142 """Exit the runtime context. This will end the transaction."""
143 if exc[0]
is None and exc[1]
is None and exc[2]
is None:
149 """Execute the SQL commands for session preparation."""
155 """Close the tough connection.
157 You can always close a tough connection with this method
158 and it will not complain if you close it more than once.
169 """Close the tough connection.
171 You are allowed to close a tough connection by default
172 and it will not complain if you close it more than once.
174 You can disallow closing connections by setting
175 the closeable parameter to something false. In this case,
176 closing tough connections will be silently ignored.
184 """Reopen the tough connection.
186 It will not complain if the connection cannot be reopened.
191 if self._transcation:
194 self.
_con.query(
'rollback')
204 """Reset the tough connection.
206 If a reset is not possible, tries to reopen the connection.
207 It will not complain if the connection is already closed.
224 """Begin a transaction."""
227 begin = self.
_con.begin
228 except AttributeError:
229 return self.
_con.query(sql
or 'begin')
233 return begin(sql=sql)
238 """Commit the current transaction."""
242 except AttributeError:
243 return self.
_con.query(sql
or 'end')
251 """Commit the current transaction."""
254 commit = self.
_con.commit
255 except AttributeError:
256 return self.
_con.query(sql
or 'commit')
264 """Rollback the current transaction."""
267 rollback = self.
_con.rollback
268 except AttributeError:
269 return self.
_con.query(sql
or 'rollback')
277 """Return a "tough" version of a connection class method.
279 The tough version checks whether the connection is bad (lost)
280 and automatically and transparently tries to reset the connection
281 if this is the case (for instance, the database has been restarted).
284 def tough_method(*args, **kwargs):
290 if not self.
_con.db.status
or (
296 result = method(*args, **kwargs)
301 elif self.
_con.db.status:
305 result = method(*args, **kwargs)
312 """Inherit the members of the standard connection class.
314 Some methods are made "tougher" than in the standard version.
317 attr = getattr(self.
_con, name)
318 if (name
in (
'query',
'get',
'insert',
'update',
'delete')
319 or name.startswith(
'get_')):
323 raise InvalidConnection
326 """Delete the steady connection."""