]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Dialect.initialize() is not called a second time if an :class:`.Engine`
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 11 Jul 2013 19:15:09 +0000 (15:15 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 11 Jul 2013 19:15:09 +0000 (15:15 -0400)
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]

doc/build/changelog/changelog_08.rst
doc/build/changelog/changelog_09.rst
lib/sqlalchemy/engine/strategies.py
lib/sqlalchemy/util/__init__.py
lib/sqlalchemy/util/langhelpers.py
test/engine/test_reconnect.py

index afb0c4991ebb852b6a808ecb4b3f4f226e171081..5c03b9007d2e7379ab4038915c0413698c012dc2 100644 (file)
@@ -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
index 21b3760b28b8433c388e073eb2e64d31968961e3..4eab720e0adb1175c59c622b710a1f8d42528077 100644 (file)
@@ -6,6 +6,15 @@
 .. changelog::
     :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
index 3ca91968b302e96aca54f402952dee12811280a7..ab9d370a3ccca6d4f534df3c232728eb6d403692 100644 (file)
@@ -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)
index 5d9c4d49ec82480ff89a239f11e25deee4ff85d0..104566215810b4b6fa7185cbce027d19a3df2863 100644 (file)
@@ -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
index c91178a75eccd56bafac119204dcd024ae039897..d63cc8e2ae098d89be976f46c7a2113e5be21d05 100644 (file)
@@ -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.
 
index 86003bec6c81054234c7b12370c8fdb3603277d0..0a964cf63501c37c1a27afc8f3450b7d54708b5a 100644 (file)
@@ -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