]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Added new flag ``retaining=False`` to the kinterbasdb and fdb dialects.
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 30 Jun 2013 23:48:48 +0000 (19:48 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 30 Jun 2013 23:48:48 +0000 (19:48 -0400)
This controls the value of the ``retaining`` flag sent to the
``commit()`` and ``rollback()`` methods of the DBAPI connection.
Defaults to False.  Also in 0.8.2, where it defaults to True.
[ticket:2763]

doc/build/changelog/changelog_08.rst
doc/build/changelog/changelog_09.rst
doc/build/changelog/migration_09.rst
lib/sqlalchemy/dialects/firebird/base.py
lib/sqlalchemy/dialects/firebird/fdb.py
lib/sqlalchemy/dialects/firebird/kinterbasdb.py
test/dialect/test_firebird.py

index d83d0618eb68dfdecb92d64396abfdd7bbb59dce..8b351957294908c328a946da6c95fbcb9eaa1ba5 100644 (file)
@@ -6,6 +6,16 @@
 .. changelog::
     :version: 0.8.2
 
+    .. change::
+        :tags: feature, firebird
+        :tickets: 2763
+
+        Added new flag ``retaining=True`` to the kinterbasdb and fdb dialects.
+        This controls the value of the ``retaining`` flag sent to the
+        ``commit()`` and ``rollback()`` methods of the DBAPI connection.
+        Due to historical concerns, this flag defaults to ``True``, however
+        in 0.9 this flag will be defaulted to ``False``.
+
     .. change::
         :tags: requirements
 
index 46644427855cbcb60a022c9739668ffbafcb583d..6909c22e008ee39ec8404d50e2f1074fb4780a99 100644 (file)
@@ -6,6 +6,15 @@
 .. changelog::
     :version: 0.9.0
 
+    .. change::
+        :tags: feature, firebird
+        :tickets: 2763
+
+        Added new flag ``retaining=False`` to the kinterbasdb and fdb dialects.
+        This controls the value of the ``retaining`` flag sent to the
+        ``commit()`` and ``rollback()`` methods of the DBAPI connection.
+        Defaults to False.  Also in 0.8.2, where it defaults to True.
+
     .. change::
         :tags: requirements
 
index 03c8563a95b41382ea093f4a6a4267039f45d7b8..127b1e69771c70bb723bb03700e2f5f0b74ade86 100644 (file)
@@ -520,4 +520,37 @@ DBAPI which per the Firebird project is now their official Python driver.
 
 :ticket:`2504`
 
+Firebird ``fdb`` and ``kinterbasdb`` set ``retaining=False`` by default
+-----------------------------------------------------------------------
+
+Both the ``fdb`` and ``kinterbasdb`` DBAPIs support a flag ``retaining=True``
+which can be passed to the ``commit()`` and ``rollback()`` methods of its
+connection.  The documented rationale for this flag is so that the DBAPI
+can re-use internal transaction state for subsequent transactions, for the
+purposes of improving performance.   However, newer documentation refers
+to analyses of Firebird's "garbage collection" which expresses that this flag
+can have a negative effect on the database's ability to process cleanup
+tasks, and has been reported as *lowering* performance as a result.
+
+It's not clear how this flag is actually usable given this information,
+and as it appears to be only a performance enhancing feature, it now defaults
+to ``False``.  The value can be controlled by passing the flag ``retaining=True``
+to the :func:`.create_engine` call.  This is a new flag which is added as of
+0.8.2, so applications on 0.8.2 can begin setting this to ``True`` or ``False``
+as desired.
+
+.. seealso::
+
+    :mod:`sqlalchemy.dialects.firebird.fdb`
+
+    :mod:`sqlalchemy.dialects.firebird.kinterbasdb`
+
+    http://pythonhosted.org/fdb/usage-guide.html#retaining-transactions - information
+    on the "retaining" flag.
+
+:ticket:`2763`
+
+
+
+
 
index ab832178eb4eb89279f52d32a1908935e5838f0b..dcaa68f4e7ba2ec0873e4037c51773500113ab2b 100644 (file)
@@ -717,15 +717,3 @@ class FBDialect(default.DefaultDialect):
 
         return list(indexes.values())
 
-    def do_execute(self, cursor, statement, parameters, context=None):
-        # kinterbase does not accept a None, but wants an empty list
-        # when there are no arguments.
-        cursor.execute(statement, parameters or [])
-
-    def do_rollback(self, dbapi_connection):
-        # Use the retaining feature, that keeps the transaction going
-        dbapi_connection.rollback(True)
-
-    def do_commit(self, dbapi_connection):
-        # Use the retaining feature, that keeps the transaction going
-        dbapi_connection.commit(True)
index 8d0bd3d782e29f6e0f54808c391f998eb86ee4bd..36b424d49bfa151edf3fa1c111d5b484822dfb9a 100644 (file)
        under the ``firebird://`` URL space, as ``fdb`` is now the official
        Python driver for Firebird.
 
-The dialect currently accepts the same arguments as the Kinterbasdb driver.
+Arguments
+----------
+
+The ``fdb`` dialect is based on the :mod:`sqlalchemy.dialects.firebird.kinterbasdb`
+dialect, however does not accept every argument that Kinterbasdb does.
+
+* ``enable_rowcount`` - True by default, setting this to False disables
+  the usage of "cursor.rowcount" with the
+  Kinterbasdb dialect, which SQLAlchemy ordinarily calls upon automatically
+  after any UPDATE or DELETE statement.   When disabled, SQLAlchemy's
+  ResultProxy will return -1 for result.rowcount.   The rationale here is
+  that Kinterbasdb requires a second round trip to the database when
+  .rowcount is called -  since SQLA's resultproxy automatically closes
+  the cursor after a non-result-returning statement, rowcount must be
+  called, if at all, before the result object is returned.   Additionally,
+  cursor.rowcount may not return correct results with older versions
+  of Firebird, and setting this flag to False will also cause the
+  SQLAlchemy ORM to ignore its usage. The behavior can also be controlled on a
+  per-execution basis using the ``enable_rowcount`` option with
+  :meth:`.Connection.execution_options`::
+
+      conn = engine.connect().execution_options(enable_rowcount=True)
+      r = conn.execute(stmt)
+      print r.rowcount
+
+* ``retaining`` - False by default.   Setting this to True will pass the
+  ``retaining=True`` keyword argument to the ``.commit()`` and ``.rollback()``
+  methods of the DBAPI connection, which can improve performance in some
+  situations, but apparently with significant caveats.
+  Please read the fdb and/or kinterbasdb DBAPI documentation in order to
+  understand the implications of this flag.
+
+  .. versionadded:: 0.8.2 - ``retaining`` keyword argument specifying
+     transaction retaining behavior - in 0.8 it defaults to ``True``
+     for backwards compatibility.
+
+  .. versionchanged:: 0.9.0 - the ``retaining`` flag defaults to ``False``.
+     In 0.8 it defaulted to ``True``.
+
+  .. seealso::
+
+    http://pythonhosted.org/fdb/usage-guide.html#retaining-transactions - information
+    on the "retaining" flag.
 
 """
 
@@ -29,6 +71,12 @@ from ... import util
 
 class FBDialect_fdb(FBDialect_kinterbasdb):
 
+    def __init__(self, enable_rowcount=True,
+                            retaining=False, **kwargs):
+        super(FBDialect_fdb, self).__init__(
+                            enable_rowcount=enable_rowcount,
+                            retaining=retaining, **kwargs)
+
     @classmethod
     def dbapi(cls):
         return  __import__('fdb')
index d581f799a32e6337444fbf326d1f6968fd668e3c..c8d8e986f5c5eba461358daa10379c371786c246 100644 (file)
 Arguments
 ----------
 
-Kinterbasedb backend specific keyword arguments are:
-
-* type_conv - select the kind of mapping done on the types: by default
-  SQLAlchemy uses 200 with Unicode, datetime and decimal support (see
-  details__).
-
-* concurrency_level - set the backend policy with regards to threading
-  issues: by default SQLAlchemy uses policy 1 (see details__).
-
-* enable_rowcount - True by default, setting this to False disables
-  the usage of "cursor.rowcount" with the
-  Kinterbasdb dialect, which SQLAlchemy ordinarily calls upon automatically
-  after any UPDATE or DELETE statement.   When disabled, SQLAlchemy's
-  ResultProxy will return -1 for result.rowcount.   The rationale here is
-  that Kinterbasdb requires a second round trip to the database when
-  .rowcount is called -  since SQLA's resultproxy automatically closes
-  the cursor after a non-result-returning statement, rowcount must be
-  called, if at all, before the result object is returned.   Additionally,
-  cursor.rowcount may not return correct results with older versions
-  of Firebird, and setting this flag to False will also cause the
-  SQLAlchemy ORM to ignore its usage. The behavior can also be controlled on a
-  per-execution basis using the `enable_rowcount` option with
-  :meth:`execution_options()`::
-
-      conn = engine.connect().execution_options(enable_rowcount=True)
-      r = conn.execute(stmt)
-      print r.rowcount
-
-__ http://sourceforge.net/projects/kinterbasdb
-__ http://firebirdsql.org/index.php?op=devel&sub=python
-__ http://kinterbasdb.sourceforge.net/dist_docs/usage.html#adv_param_conv_dynamic_type_translation
-__ http://kinterbasdb.sourceforge.net/dist_docs/usage.html#special_issue_concurrency
+The Kinterbasdb backend accepts the ``enable_rowcount`` and ``retaining``
+arguments accepted by the :mod:`sqlalchemy.dialects.firebird.fdb` dialect.   In addition, it
+also accepts the following:
+
+* ``type_conv`` - select the kind of mapping done on the types: by default
+  SQLAlchemy uses 200 with Unicode, datetime and decimal support.  See
+  the linked documents below for further information.
+
+* ``concurrency_level`` - set the backend policy with regards to threading
+  issues: by default SQLAlchemy uses policy 1.  See the linked documents
+  below for futher information.
+
+.. seealso::
+
+    http://sourceforge.net/projects/kinterbasdb
+
+    http://kinterbasdb.sourceforge.net/dist_docs/usage.html#adv_param_conv_dynamic_type_translation
+
+    http://kinterbasdb.sourceforge.net/dist_docs/usage.html#special_issue_concurrency
+
 """
 
 from .base import FBDialect, FBExecutionContext
@@ -91,11 +79,13 @@ class FBDialect_kinterbasdb(FBDialect):
     )
 
     def __init__(self, type_conv=200, concurrency_level=1,
-                            enable_rowcount=True, **kwargs):
+                            enable_rowcount=True,
+                            retaining=False, **kwargs):
         super(FBDialect_kinterbasdb, self).__init__(**kwargs)
         self.enable_rowcount = enable_rowcount
         self.type_conv = type_conv
         self.concurrency_level = concurrency_level
+        self.retaining = retaining
         if enable_rowcount:
             self.supports_sane_rowcount = True
 
@@ -103,6 +93,17 @@ class FBDialect_kinterbasdb(FBDialect):
     def dbapi(cls):
         return __import__('kinterbasdb')
 
+    def do_execute(self, cursor, statement, parameters, context=None):
+        # kinterbase does not accept a None, but wants an empty list
+        # when there are no arguments.
+        cursor.execute(statement, parameters or [])
+
+    def do_rollback(self, dbapi_connection):
+        dbapi_connection.rollback(self.retaining)
+
+    def do_commit(self, dbapi_connection):
+        dbapi_connection.commit(self.retaining)
+
     def create_connect_args(self, url):
         opts = url.translate_connect_args(username='user')
         if opts.get('port'):
index 6019dc8f905e1e39a30037b4739ccdf28404d92d..4a71b7d050817b0ec53eb8dc0771d69c3e81d443 100644 (file)
@@ -444,3 +444,76 @@ class MiscTest(fixtures.TestBase):
             (text("select 'hello % world' from rdb$database"),
              'hello % world'):
             eq_(testing.db.scalar(expr), result)
+
+from sqlalchemy.testing.mock import Mock, call
+
+
+class ArgumentTest(fixtures.TestBase):
+    def _dbapi(self):
+        return Mock(
+                    paramstyle='qmark',
+                    connect=Mock(
+                            return_value=Mock(
+                                server_version="UI-V6.3.2.18118 Firebird 2.1",
+                                cursor=Mock(return_value=Mock())
+                            )
+                    )
+                )
+
+    def _engine(self, type_, **kw):
+        dbapi = self._dbapi()
+        kw.update(
+            dict(
+                    module=dbapi,
+                    _initialize=False
+            )
+        )
+        engine = engines.testing_engine("firebird+%s://" % type_,
+                                options=kw)
+        return engine
+
+    def test_retaining_flag_default_kinterbasdb(self):
+        engine = self._engine("kinterbasdb")
+        self._assert_retaining(engine, False)
+
+    def test_retaining_flag_true_kinterbasdb(self):
+        engine = self._engine("kinterbasdb", retaining=True)
+        self._assert_retaining(engine, True)
+
+    def test_retaining_flag_false_kinterbasdb(self):
+        engine = self._engine("kinterbasdb", retaining=False)
+        self._assert_retaining(engine, False)
+
+    def test_retaining_flag_default_fdb(self):
+        engine = self._engine("fdb")
+        self._assert_retaining(engine, False)
+
+    def test_retaining_flag_true_fdb(self):
+        engine = self._engine("fdb", retaining=True)
+        self._assert_retaining(engine, True)
+
+    def test_retaining_flag_false_fdb(self):
+        engine = self._engine("fdb", retaining=False)
+        self._assert_retaining(engine, False)
+
+
+    def _assert_retaining(self, engine, flag):
+        conn = engine.connect()
+        trans = conn.begin()
+        trans.commit()
+        eq_(
+            engine.dialect.dbapi.connect.return_value.commit.mock_calls,
+            [call(flag)]
+        )
+
+        trans = conn.begin()
+        trans.rollback()
+        eq_(
+            engine.dialect.dbapi.connect.return_value.rollback.mock_calls,
+            [call(flag)]
+        )
+
+
+
+
+