summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2013-07-11 15:15:09 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2013-07-11 15:15:09 -0400
commitfd55be01ddc0ab41dd9469c6c7736d12d5a2f1ea (patch)
tree460612226d5710aaf151f7552fcba6c1f7cbcd42
parentcd8a40284d0fb053f652f4a3d2745c22674603f8 (diff)
downloadsqlalchemy-fd55be01ddc0ab41dd9469c6c7736d12d5a2f1ea.tar.gz
Dialect.initialize() is not called a second time if an :class:`.Engine`
is recreated, due to a disconnect error. This fixes a particular issue in the Oracle 8 dialect, but in general the dialect.initialize() phase should only be once per dialect. Also in 0.8.3. [ticket:2776]
-rw-r--r--doc/build/changelog/changelog_08.rst7
-rw-r--r--doc/build/changelog/changelog_09.rst9
-rw-r--r--lib/sqlalchemy/engine/strategies.py1
-rw-r--r--lib/sqlalchemy/util/__init__.py2
-rw-r--r--lib/sqlalchemy/util/langhelpers.py14
-rw-r--r--test/engine/test_reconnect.py34
6 files changed, 63 insertions, 4 deletions
diff --git a/doc/build/changelog/changelog_08.rst b/doc/build/changelog/changelog_08.rst
index afb0c4991..5c03b9007 100644
--- a/doc/build/changelog/changelog_08.rst
+++ b/doc/build/changelog/changelog_08.rst
@@ -6,6 +6,13 @@
.. changelog::
:version: 0.8.3
+ :tickets: 2776
+
+ Dialect.initialize() is not called a second time if an :class:`.Engine`
+ is recreated, due to a disconnect error. This fixes a particular
+ issue in the Oracle 8 dialect, but in general the dialect.initialize()
+ phase should only be once per dialect.
+
.. change::
:tags: feature, sql
:tickets: 722
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst
index 21b3760b2..4eab720e0 100644
--- a/doc/build/changelog/changelog_09.rst
+++ b/doc/build/changelog/changelog_09.rst
@@ -7,6 +7,15 @@
:version: 0.9.0
.. change::
+ :tags: bug, engine, oracle
+ :tickets: 2776
+
+ Dialect.initialize() is not called a second time if an :class:`.Engine`
+ is recreated, due to a disconnect error. This fixes a particular
+ issue in the Oracle 8 dialect, but in general the dialect.initialize()
+ phase should only be once per dialect. Also in 0.8.3.
+
+ .. change::
:tags: feature, sql
:tickets: 722
diff --git a/lib/sqlalchemy/engine/strategies.py b/lib/sqlalchemy/engine/strategies.py
index 3ca91968b..ab9d370a3 100644
--- a/lib/sqlalchemy/engine/strategies.py
+++ b/lib/sqlalchemy/engine/strategies.py
@@ -149,6 +149,7 @@ class DefaultEngineStrategy(EngineStrategy):
event.listen(pool, 'first_connect', on_connect)
event.listen(pool, 'connect', on_connect)
+ @util.only_once
def first_connect(dbapi_connection, connection_record):
c = base.Connection(engine, connection=dbapi_connection,
_has_events=False)
diff --git a/lib/sqlalchemy/util/__init__.py b/lib/sqlalchemy/util/__init__.py
index 5d9c4d49e..104566215 100644
--- a/lib/sqlalchemy/util/__init__.py
+++ b/lib/sqlalchemy/util/__init__.py
@@ -31,7 +31,7 @@ from .langhelpers import iterate_attributes, class_hierarchy, \
classproperty, set_creation_order, warn_exception, warn, NoneType,\
constructor_copy, methods_equivalent, chop_traceback, asint,\
generic_repr, counter, PluginLoader, hybridmethod, safe_reraise,\
- get_callable_argspec
+ get_callable_argspec, only_once
from .deprecations import warn_deprecated, warn_pending_deprecation, \
deprecated, pending_deprecation, inject_docstring_text
diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py
index c91178a75..d63cc8e2a 100644
--- a/lib/sqlalchemy/util/langhelpers.py
+++ b/lib/sqlalchemy/util/langhelpers.py
@@ -1047,6 +1047,20 @@ _SQLA_RE = re.compile(r'sqlalchemy/([a-z_]+/){0,2}[a-z_]+\.py')
_UNITTEST_RE = re.compile(r'unit(?:2|test2?/)')
+def only_once(fn):
+ """Decorate the given function to be a no-op after it is called exactly
+ once."""
+
+ once = [fn]
+ def go(*arg, **kw):
+ if once:
+ once_fn = once.pop()
+ return once_fn(*arg, **kw)
+
+ return update_wrapper(go, fn)
+
+
+
def chop_traceback(tb, exclude_prefix=_UNITTEST_RE, exclude_suffix=_SQLA_RE):
"""Chop extraneous lines off beginning and end of a traceback.
diff --git a/test/engine/test_reconnect.py b/test/engine/test_reconnect.py
index 86003bec6..0a964cf63 100644
--- a/test/engine/test_reconnect.py
+++ b/test/engine/test_reconnect.py
@@ -346,6 +346,28 @@ class MockReconnectTest(fixtures.TestBase):
list, result
)
+ def test_dialect_initialize_once(self):
+ from sqlalchemy.engine.base import Engine
+ from sqlalchemy.engine.url import URL
+ from sqlalchemy.engine.default import DefaultDialect
+ from sqlalchemy.pool import QueuePool
+ dbapi = self.dbapi
+
+ mock_dialect = Mock()
+ class MyURL(URL):
+ def get_dialect(self):
+ return Dialect
+ class Dialect(DefaultDialect):
+ initialize = Mock()
+
+ engine = create_engine(MyURL("foo://"), module=dbapi)
+ c1 = engine.connect()
+ engine.dispose()
+ c2 = engine.connect()
+ eq_(Dialect.initialize.call_count, 1)
+
+
+
class CursorErrTest(fixtures.TestBase):
def setup(self):
@@ -494,9 +516,15 @@ class RealReconnectTest(fixtures.TestBase):
# raises a DBAPIError, not an AttributeError
assert_raises(exc.DBAPIError, engine.connect)
- # dispose connections so we get a new one on
- # next go
- engine.dispose()
+ @testing.skip_if(
+ [lambda: util.py3k, "oracle+cx_oracle"],
+ "Crashes on py3k+cx_oracle")
+ def test_explode_in_initializer_disconnect(self):
+ engine = engines.testing_engine()
+ def broken_initialize(connection):
+ connection.execute("select fake_stuff from _fake_table")
+
+ engine.dialect.initialize = broken_initialize
p1 = engine.pool