[3] | 1 | # interfaces.py |
---|
| 2 | # Copyright (C) 2007 Jason Kirtland jek@discorporate.us |
---|
| 3 | # |
---|
| 4 | # This module is part of SQLAlchemy and is released under |
---|
| 5 | # the MIT License: http://www.opensource.org/licenses/mit-license.php |
---|
| 6 | |
---|
| 7 | """Interfaces and abstract types.""" |
---|
| 8 | |
---|
| 9 | |
---|
| 10 | class PoolListener(object): |
---|
| 11 | """Hooks into the lifecycle of connections in a ``Pool``. |
---|
| 12 | |
---|
| 13 | Usage:: |
---|
| 14 | |
---|
| 15 | class MyListener(PoolListener): |
---|
| 16 | def connect(self, dbapi_con, con_record): |
---|
| 17 | '''perform connect operations''' |
---|
| 18 | # etc. |
---|
| 19 | |
---|
| 20 | # create a new pool with a listener |
---|
| 21 | p = QueuePool(..., listeners=[MyListener()]) |
---|
| 22 | |
---|
| 23 | # add a listener after the fact |
---|
| 24 | p.add_listener(MyListener()) |
---|
| 25 | |
---|
| 26 | # usage with create_engine() |
---|
| 27 | e = create_engine("url://", listeners=[MyListener()]) |
---|
| 28 | |
---|
| 29 | All of the standard connection :class:`~sqlalchemy.pool.Pool` types can |
---|
| 30 | accept event listeners for key connection lifecycle events: |
---|
| 31 | creation, pool check-out and check-in. There are no events fired |
---|
| 32 | when a connection closes. |
---|
| 33 | |
---|
| 34 | For any given DB-API connection, there will be one ``connect`` |
---|
| 35 | event, `n` number of ``checkout`` events, and either `n` or `n - 1` |
---|
| 36 | ``checkin`` events. (If a ``Connection`` is detached from its |
---|
| 37 | pool via the ``detach()`` method, it won't be checked back in.) |
---|
| 38 | |
---|
| 39 | These are low-level events for low-level objects: raw Python |
---|
| 40 | DB-API connections, without the conveniences of the SQLAlchemy |
---|
| 41 | ``Connection`` wrapper, ``Dialect`` services or ``ClauseElement`` |
---|
| 42 | execution. If you execute SQL through the connection, explicitly |
---|
| 43 | closing all cursors and other resources is recommended. |
---|
| 44 | |
---|
| 45 | Events also receive a ``_ConnectionRecord``, a long-lived internal |
---|
| 46 | ``Pool`` object that basically represents a "slot" in the |
---|
| 47 | connection pool. ``_ConnectionRecord`` objects have one public |
---|
| 48 | attribute of note: ``info``, a dictionary whose contents are |
---|
| 49 | scoped to the lifetime of the DB-API connection managed by the |
---|
| 50 | record. You can use this shared storage area however you like. |
---|
| 51 | |
---|
| 52 | There is no need to subclass ``PoolListener`` to handle events. |
---|
| 53 | Any class that implements one or more of these methods can be used |
---|
| 54 | as a pool listener. The ``Pool`` will inspect the methods |
---|
| 55 | provided by a listener object and add the listener to one or more |
---|
| 56 | internal event queues based on its capabilities. In terms of |
---|
| 57 | efficiency and function call overhead, you're much better off only |
---|
| 58 | providing implementations for the hooks you'll be using. |
---|
| 59 | |
---|
| 60 | """ |
---|
| 61 | |
---|
| 62 | def connect(self, dbapi_con, con_record): |
---|
| 63 | """Called once for each new DB-API connection or Pool's ``creator()``. |
---|
| 64 | |
---|
| 65 | dbapi_con |
---|
| 66 | A newly connected raw DB-API connection (not a SQLAlchemy |
---|
| 67 | ``Connection`` wrapper). |
---|
| 68 | |
---|
| 69 | con_record |
---|
| 70 | The ``_ConnectionRecord`` that persistently manages the connection |
---|
| 71 | |
---|
| 72 | """ |
---|
| 73 | |
---|
| 74 | def checkout(self, dbapi_con, con_record, con_proxy): |
---|
| 75 | """Called when a connection is retrieved from the Pool. |
---|
| 76 | |
---|
| 77 | dbapi_con |
---|
| 78 | A raw DB-API connection |
---|
| 79 | |
---|
| 80 | con_record |
---|
| 81 | The ``_ConnectionRecord`` that persistently manages the connection |
---|
| 82 | |
---|
| 83 | con_proxy |
---|
| 84 | The ``_ConnectionFairy`` which manages the connection for the span of |
---|
| 85 | the current checkout. |
---|
| 86 | |
---|
| 87 | If you raise an ``exc.DisconnectionError``, the current |
---|
| 88 | connection will be disposed and a fresh connection retrieved. |
---|
| 89 | Processing of all checkout listeners will abort and restart |
---|
| 90 | using the new connection. |
---|
| 91 | """ |
---|
| 92 | |
---|
| 93 | def checkin(self, dbapi_con, con_record): |
---|
| 94 | """Called when a connection returns to the pool. |
---|
| 95 | |
---|
| 96 | Note that the connection may be closed, and may be None if the |
---|
| 97 | connection has been invalidated. ``checkin`` will not be called |
---|
| 98 | for detached connections. (They do not return to the pool.) |
---|
| 99 | |
---|
| 100 | dbapi_con |
---|
| 101 | A raw DB-API connection |
---|
| 102 | |
---|
| 103 | con_record |
---|
| 104 | The ``_ConnectionRecord`` that persistently manages the connection |
---|
| 105 | |
---|
| 106 | """ |
---|
| 107 | |
---|
| 108 | class ConnectionProxy(object): |
---|
| 109 | """Allows interception of statement execution by Connections. |
---|
| 110 | |
---|
| 111 | Either or both of the ``execute()`` and ``cursor_execute()`` |
---|
| 112 | may be implemented to intercept compiled statement and |
---|
| 113 | cursor level executions, e.g.:: |
---|
| 114 | |
---|
| 115 | class MyProxy(ConnectionProxy): |
---|
| 116 | def execute(self, conn, execute, clauseelement, *multiparams, **params): |
---|
| 117 | print "compiled statement:", clauseelement |
---|
| 118 | return execute(clauseelement, *multiparams, **params) |
---|
| 119 | |
---|
| 120 | def cursor_execute(self, execute, cursor, statement, parameters, context, executemany): |
---|
| 121 | print "raw statement:", statement |
---|
| 122 | return execute(cursor, statement, parameters, context) |
---|
| 123 | |
---|
| 124 | The ``execute`` argument is a function that will fulfill the default |
---|
| 125 | execution behavior for the operation. The signature illustrated |
---|
| 126 | in the example should be used. |
---|
| 127 | |
---|
| 128 | The proxy is installed into an :class:`~sqlalchemy.engine.Engine` via |
---|
| 129 | the ``proxy`` argument:: |
---|
| 130 | |
---|
| 131 | e = create_engine('someurl://', proxy=MyProxy()) |
---|
| 132 | |
---|
| 133 | """ |
---|
| 134 | def execute(self, conn, execute, clauseelement, *multiparams, **params): |
---|
| 135 | """Intercept high level execute() events.""" |
---|
| 136 | |
---|
| 137 | return execute(clauseelement, *multiparams, **params) |
---|
| 138 | |
---|
| 139 | def cursor_execute(self, execute, cursor, statement, parameters, context, executemany): |
---|
| 140 | """Intercept low-level cursor execute() events.""" |
---|
| 141 | |
---|
| 142 | return execute(cursor, statement, parameters, context) |
---|
| 143 | |
---|
| 144 | |
---|