but we'd like to. Most DBAPIs don't give us anything we can do with it.
Some research was done on psycopg2 and it still seems like they give us
no adequate method (tried connection.closed, cursor.closed, connection.status).
mxodbc claims their .closed attribute will work (but I am skeptical).
- remove beahvior in pool that auto-invalidated a connection when
the cursor failed to create. That's not the pool's job. we need the conn
for the error logic. Can't get any tests to fail, curious why that
behavior was there, guess we'll find out (or not).
- add support for psycopg2 version detection. even though we have
no use for it yet...
- adjust one of the reconnect tests to work with oracle's
horrendously slow connect speed
opts.pop('database', None)
return (args,), opts
- def is_disconnect(self, e):
- # eGenix recommends checking connection.closed here,
- # but how can we get a handle on the current connection?
+ def is_disconnect(self, e, connection, cursor):
+ # TODO: eGenix recommends checking connection.closed here
+ # Does that detect dropped connections ?
if isinstance(e, self.dbapi.ProgrammingError):
return "connection already closed" in str(e)
elif isinstance(e, self.dbapi.Error):
connectors.extend(['%s=%s' % (k,v) for k,v in keys.iteritems()])
return [[";".join (connectors)], connect_args]
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
if isinstance(e, self.dbapi.ProgrammingError):
return "The cursor's connection has been closed." in str(e) or \
'Attempt to use a closed connection.' in str(e)
self.jdbc_driver_name],
opts]
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
if not isinstance(e, self.dbapi.ProgrammingError):
return False
e = str(e)
else:
return tuple([int(x) for x in m.group(1, 2, 3)] + ['interbase'])
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
if isinstance(e, (self.dbapi.OperationalError,
self.dbapi.ProgrammingError)):
msg = str(e)
v = VERSION_RE.split(connection.connection.dbms_version)
return (int(v[1]), int(v[2]), v[3])
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
if isinstance(e, self.dbapi.OperationalError):
return 'closed the connection' in str(e) \
or 'connection not open' in str(e)
connectors.append("Integrated Security=SSPI")
return [[";".join (connectors)], {}]
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
return isinstance(e, self.dbapi.adodbapi.DatabaseError) and \
"'connection failure'" in str(e)
opts['host'] = "%s:%s" % (opts['host'], port)
return [[], opts]
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
for msg in (
"Error 10054",
"Not connected to any MS SQL server",
resultset = connection.execute("XA RECOVER")
return [row['data'][0:row['gtrid_length']] for row in resultset]
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
if isinstance(e, self.dbapi.OperationalError):
return self._extract_error_code(e) in \
(2006, 2013, 2014, 2045, 2055)
def _extract_error_code(self, exception):
return exception.errno
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
errnos = (2006, 2013, 2014, 2045, 2055, 2048)
exceptions = (self.dbapi.OperationalError,self.dbapi.InterfaceError)
if isinstance(e, exceptions):
execution_options(_oursql_plain_query=True),
table, charset, full_name)
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
if isinstance(e, self.dbapi.ProgrammingError):
return e.errno is None and 'cursor' not in e.args[1] and e.args[1].endswith('closed')
else:
for x in connection.connection.version.split('.')
)
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
if isinstance(e, self.dbapi.InterfaceError):
return "not connected" in str(e)
else:
opts.update(url.query)
return ([], opts)
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
return "connection is closed" in str(e)
dialect = PGDialect_pg8000
execution_ctx_cls = PGExecutionContext_psycopg2
statement_compiler = PGCompiler_psycopg2
preparer = PGIdentifierPreparer_psycopg2
+ psycopg2_version = (0, 0)
colspecs = util.update_copy(
PGDialect.colspecs,
self.server_side_cursors = server_side_cursors
self.use_native_unicode = use_native_unicode
self.supports_unicode_binds = use_native_unicode
+ if self.dbapi and hasattr(self.dbapi, '__version__'):
+ m = re.match(r'(\d+)\.(\d+)\.(\d+)?',
+ self.dbapi.__version__)
+ if m:
+ self.psycopg2_version = tuple(map(int, m.group(1, 2, 3)))
@classmethod
def dbapi(cls):
opts.update(url.query)
return ([], opts)
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
if isinstance(e, self.dbapi.OperationalError):
# these error messages from libpq: interfaces/libpq/fe-misc.c.
# TODO: these are sent through gettext in libpq and we can't
opts.update(url.query)
return ([], opts)
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
return "connection is closed" in str(e)
dialect = PGDialect_pypostgresql
return ([filename], opts)
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
return isinstance(e, self.dbapi.ProgrammingError) and "Cannot operate on a closed database." in str(e)
dialect = SQLiteDialect_pysqlite
# (12, 5, 0, 0)
return (vers / 1000, vers % 1000 / 100, vers % 100 / 10, vers % 10)
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
if isinstance(e, (self.dbapi.OperationalError,
self.dbapi.ProgrammingError)):
msg = str(e)
raise NotImplementedError()
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
"""Return True if the given DB-API error indicates an invalid
connection"""
if context:
context.handle_dbapi_exception(e)
- is_disconnect = self.dialect.is_disconnect(e)
+ is_disconnect = self.dialect.is_disconnect(e, self.__connection, cursor)
if is_disconnect:
self.invalidate(e)
self.engine.dispose()
def do_execute(self, cursor, statement, parameters, context=None):
cursor.execute(statement, parameters)
- def is_disconnect(self, e):
+ def is_disconnect(self, e, connection, cursor):
return False
def reset_isolation_level(self, dbapi_conn):
self._close()
def cursor(self, *args, **kwargs):
- try:
- return self.connection.cursor(*args, **kwargs)
- except Exception, e:
- self.invalidate(e=e)
- raise
+ return self.connection.cursor(*args, **kwargs)
def __getattr__(self, key):
return getattr(self.connection, key)
module=dbapi, _initialize=False)
# monkeypatch disconnect checker
- db.dialect.is_disconnect = lambda e: isinstance(e, MockDisconnect)
+ db.dialect.is_disconnect = lambda e, conn, cursor: isinstance(e, MockDisconnect)
def test_reconnect(self):
"""test that an 'is_disconnect' condition will invalidate the
conn.close()
+ def test_ensure_is_disconnect_gets_connection(self):
+ def is_disconnect(e, conn, cursor):
+ # connection is still present
+ assert conn.connection is not None
+ # the error usually occurs on connection.cursor(),
+ # though MySQLdb we get a non-working cursor.
+ # assert cursor is None
+
+ engine.dialect.is_disconnect = is_disconnect
+ conn = engine.connect()
+ engine.test_shutdown()
+ assert_raises(
+ tsa.exc.DBAPIError,
+ conn.execute, select([1])
+ )
+
def test_invalidate_twice(self):
conn = engine.connect()
conn.invalidate()
p1 = engine.pool
- def is_disconnect(e):
+ def is_disconnect(e, conn, cursor):
return True
engine.dialect.is_disconnect = is_disconnect
def test_basic(self):
for threadlocal in False, True:
- engine = engines.reconnecting_engine(options={'pool_recycle'
- : 1, 'pool_threadlocal': threadlocal})
+ engine = engines.reconnecting_engine(
+ options={'pool_threadlocal': threadlocal})
+
conn = engine.contextual_connect()
eq_(conn.execute(select([1])).scalar(), 1)
conn.close()
+
+ # set the pool recycle down to 1.
+ # we aren't doing this inline with the
+ # engine create since cx_oracle takes way
+ # too long to create the 1st connection and don't
+ # want to build a huge delay into this test.
+
+ engine.pool._recycle = 1
+
+ # kill the DB connection
engine.test_shutdown()
+
+ # wait until past the recycle period
time.sleep(2)
+
+ # can connect, no exception
conn = engine.contextual_connect()
eq_(conn.execute(select([1])).scalar(), 1)
conn.close()