]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- move documentation of available execution options to Connection - this is the main
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 17 Apr 2011 20:10:59 +0000 (16:10 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 17 Apr 2011 20:10:59 +0000 (16:10 -0400)
place these should be used
- Executable disallows "compiled_cache" option for now which was previously being ignored
[ticket:2131]
- Query now passes execution options to the Connection rather than the statement
so that all options are allowed including compiled cache.

CHANGES
lib/sqlalchemy/engine/base.py
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/sql/expression.py
test/orm/test_query.py
test/sql/test_generative.py

diff --git a/CHANGES b/CHANGES
index 9f2d1c31a6975e863ec5ccb5f8d8cb3fb6c245af..29fdb757e26a08d673bbb7871a1db287d7309f96 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -74,7 +74,20 @@ CHANGES
     an 0.7 style count() query [ticket:2130].
     (also in 0.6.7)
 
+  - the Query.execution_options() method now passes
+    those options to the Connection rather than
+    the SELECT statement, so that all available
+    options including isolation level and 
+    compiled cache may be used.  [ticket:2131]
+
 - sql
+  - The "compiled_cache" execution option now raises
+    an error when passed to a SELECT statement 
+    rather than a Connection.  Previously it was
+    being ignored entirely.   We may look into
+    having this option work on a per-statement
+    level at some point. [ticket:2131]
+
   - Restored the "catchall" constructor on the base
     TypeEngine class, with a deprecation warning.
     This so that code which does something like
index 64dc653caeada77002e3b7a9c74f0e2583ce7e9c..00eae60a1c4e93d48427ef35b349d55e9de2c426 100644 (file)
@@ -894,8 +894,40 @@ class Connection(Connectable):
         accepted by :meth:`.Executable.execution_options`.  Additionally,
         it includes options that are applicable only to 
         :class:`.Connection`.
+
+        :param autocommit: Available on: Connection, statement.
+          When True, a COMMIT will be invoked after execution 
+          when executed in 'autocommit' mode, i.e. when an explicit
+          transaction is not begun on the connection. Note that DBAPI
+          connections by default are always in a transaction - SQLAlchemy uses
+          rules applied to different kinds of statements to determine if
+          COMMIT will be invoked in order to provide its "autocommit" feature.
+          Typically, all INSERT/UPDATE/DELETE statements as well as
+          CREATE/DROP statements have autocommit behavior enabled; SELECT
+          constructs do not. Use this option when invoking a SELECT or other
+          specific SQL construct where COMMIT is desired (typically when
+          calling stored procedures and such), and an explicit
+          transaction is not in progress.
+
+        :param compiled_cache: Available on: Connection.
+          A dictionary where :class:`.Compiled` objects
+          will be cached when the :class:`.Connection` compiles a clause 
+          expression into a :class:`.Compiled` object.   
+          It is the user's responsibility to
+          manage the size of this dictionary, which will have keys
+          corresponding to the dialect, clause element, the column
+          names within the VALUES or SET clause of an INSERT or UPDATE, 
+          as well as the "batch" mode for an INSERT or UPDATE statement.
+          The format of this dictionary is not guaranteed to stay the
+          same in future releases.
+
+          Note that the ORM makes use of its own "compiled" caches for 
+          some operations, including flush operations.  The caching
+          used by the ORM internally supersedes a cache dictionary
+          specified here.
         
-        :param isolation_level: Set the transaction isolation level for
+        :param isolation_level: Available on: Connection.
+          Set the transaction isolation level for
           the lifespan of this connection.   Valid values include
           those string values accepted by the ``isolation_level``
           parameter passed to :func:`.create_engine`, and are
@@ -910,8 +942,11 @@ class Connection(Connectable):
           is returned to the connection pool, i.e.
           the :meth:`.Connection.close` method is called.
 
-        :param \**kw: All options accepted by :meth:`.Executable.execution_options`
-          are also accepted.
+        :param stream_results: Available on: Connection, statement.
+          Indicate to the dialect that results should be 
+          "streamed" and not pre-buffered, if possible.  This is a limitation
+          of many DBAPIs.  The flag is currently understood only by the
+          psycopg2 dialect.
 
         """
         c = self._clone()
index ed6d02f2ced79bb5ad98ff2052bf7591747912e4..07df0de42967e4cdf7d2a8b09ccdee8875e02a7a 100644 (file)
@@ -920,7 +920,7 @@ class Query(object):
         """ Set non-SQL options which take effect during execution.
 
         The options are the same as those accepted by 
-        :meth:`sqlalchemy.sql.expression.Executable.execution_options`.
+        :meth:`.Connection.execution_options`.
 
         Note that the ``stream_results`` execution option is enabled
         automatically if the :meth:`~sqlalchemy.orm.query.Query.yield_per()`
@@ -1749,10 +1749,14 @@ class Query(object):
         return self._execute_and_instances(context)
 
     def _execute_and_instances(self, querycontext):
-        result = self.session.connection(
+        conn = self.session.connection(
                         mapper = self._mapper_zero_or_none(),
                         clause = querycontext.statement,
-                        close_with_result=True).execute(querycontext.statement, self._params)
+                        close_with_result=True)
+        if self._execution_options:
+            conn = conn.execution_options(**self._execution_options)
+
+        result = conn.execute(querycontext.statement, self._params)
         return self.instances(result, querycontext)
 
     @property
@@ -2448,10 +2452,6 @@ class Query(object):
                                 for_update=for_update, 
                                 use_labels=labels)
 
-            if self._execution_options:
-                statement = statement.execution_options(
-                                                **self._execution_options)
-
             from_clause = inner
             for eager_join in eager_joins:
                 # EagerLoader places a 'stop_on' attribute on the join,
@@ -2501,10 +2501,6 @@ class Query(object):
             for hint in self._with_hints:
                 statement = statement.with_hint(*hint)
 
-            if self._execution_options:
-                statement = statement.execution_options(
-                                            **self._execution_options)
-
             if self._correlate:
                 statement = statement.correlate(*self._correlate)
 
index c552f055f39921e5ef7528ef6d4a24ea75bd3c94..1013f9397b0e08540a1dc4417881d1a9c1beff4a 100644 (file)
@@ -2678,55 +2678,29 @@ class Executable(_Generative):
     def execution_options(self, **kw):
         """ Set non-SQL options for the statement which take effect during
         execution.
-
-        :param autocommit: when True, a COMMIT will be invoked after execution 
-          when executed in 'autocommit' mode, i.e. when an explicit
-          transaction is not begun on the connection. Note that DBAPI
-          connections by default are always in a transaction - SQLAlchemy uses
-          rules applied to different kinds of statements to determine if
-          COMMIT will be invoked in order to provide its "autocommit" feature.
-          Typically, all INSERT/UPDATE/DELETE statements as well as
-          CREATE/DROP statements have autocommit behavior enabled; SELECT
-          constructs do not. Use this option when invoking a SELECT or other
-          specific SQL construct where COMMIT is desired (typically when
-          calling stored procedures and such).
-
-        :param stream_results: indicate to the dialect that results should be 
-          "streamed" and not pre-buffered, if possible.  This is a limitation
-          of many DBAPIs.  The flag is currently understood only by the
-          psycopg2 dialect.
-
-        :param compiled_cache: a dictionary where :class:`.Compiled` objects
-          will be cached when the :class:`.Connection` compiles a clause 
-          expression into a dialect- and parameter-specific 
-          :class:`.Compiled` object.   It is the user's responsibility to
-          manage the size of this dictionary, which will have keys
-          corresponding to the dialect, clause element, the column
-          names within the VALUES or SET clause of an INSERT or UPDATE, 
-          as well as the "batch" mode for an INSERT or UPDATE statement.
-          The format of this dictionary is not guaranteed to stay the
-          same in future releases.
-
-          This option is usually more appropriate
-          to use via the 
-          :meth:`.Connection.execution_options()`
-          method of :class:`.Connection`, rather than upon individual 
-          statement objects, though the effect is the same.
-          
-          Note that the ORM makes use of its own "compiled" caches for 
-          some operations, including flush operations.  The caching
-          used by the ORM internally supersedes a cache dictionary
-          specified here.
+        
+        Execution options can be set on a per-statement or 
+        per :class:`.Connection` basis.   Additionally, the 
+        :class:`.Engine` and ORM :class:`~.orm.query.Query` objects provide access
+        to execution options which they in turn configure upon connections.
+        
+        The :meth:`execution_options` method is generative.  A new 
+        instance of this statement is returned that contains the options::
+        
+            statement = select([table.c.x, table.c.y])
+            statement = statement.execution_options(autocommit=True)
+        
+        Note that only a subset of possible execution options can be applied
+        to a statement - these include "autocommit" and "stream_results",
+        but not "isolation_level" or "compiled_cache".  
+        See :meth:`.Connection.execution_options` for a full list of 
+        possible options.
 
         See also:
 
-            :meth:`.Connection.execution_options()` -
-            includes a connection-only option to specify transaction isolation
-            level.
+            :meth:`.Connection.execution_options()`
 
-            :meth:`.Query.execution_options()` - applies options to 
-                the statement
-            generated by a :class:`.orm.Query` object.
+            :meth:`.Query.execution_options()`
 
         """
         if 'isolation_level' in kw:
@@ -2736,7 +2710,11 @@ class Executable(_Generative):
                 "per-engine using the isolation_level "
                 "argument to create_engine()."
             )
-
+        if 'compiled_cache' in kw:
+            raise exc.ArgumentError(
+                "'compiled_cache' execution option may only be specified "
+                "on Connection.execution_options(), not per statement."
+            )
         self._execution_options = self._execution_options.union(kw)
 
     def execute(self, *multiparams, **params):
index 946f24a5d1792659dbf60a50ca830614ea11435f..838bbcde063561b05cd333a16ac8c33d5b3ab9d1 100644 (file)
@@ -1790,9 +1790,9 @@ class ImmediateTest(_fixtures.FixtureTest):
         sess.bind = testing.db
         eq_(sess.query().value(sa.literal_column('1').label('x')), 1)
 
-class StatementOptionsTest(QueryTest):
+class ExecutionOptionsTest(QueryTest):
 
-    def test_query_with_statement_option(self):
+    def test_option_building(self):
         User = self.classes.User
 
         sess = create_session(bind=testing.db, autocommit=False)
@@ -1809,13 +1809,22 @@ class StatementOptionsTest(QueryTest):
 
         q3_options = dict(foo='not bar', stream_results=True, answer=42)
         assert q3._execution_options == q3_options
-        assert q3.statement._execution_options == q3_options
-        assert q3._compile_context().statement._execution_options == q3_options
-        assert q3.subquery().original._execution_options == q3_options
 
-    # TODO: Test that statement options are passed on to
-    # updates/deletes, but currently there are no such options
-    # applicable for them.
+    def test_options_in_connection(self):
+        User = self.classes.User
+
+        execution_options = dict(foo='bar', stream_results=True)
+        class TQuery(Query):
+            def instances(self, result, ctx):
+                eq_(
+                    result.connection._execution_options,
+                    execution_options
+                )
+                return iter([])
+
+        sess = create_session(bind=testing.db, autocommit=False, query_cls=TQuery)
+        q1 = sess.query(User).execution_options(**execution_options)
+        q1.all()
 
 class OptionsTest(QueryTest):
     """Test the _get_paths() method of PropertyOption."""
index 0634ded40bdcd8e39de2f57f96e6759f907c06f3..47e45bbb942295f07c4ac6566693547f3d3dd2ef 100644 (file)
@@ -3,9 +3,9 @@ from sqlalchemy.sql import table, column, ClauseElement
 from sqlalchemy.sql.expression import  _clone, _from_objects
 from test.lib import *
 from sqlalchemy.sql.visitors import *
-from sqlalchemy import util
+from sqlalchemy import util, exc
 from sqlalchemy.sql import util as sql_util
-from test.lib.testing import eq_
+from test.lib.testing import eq_, assert_raises
 
 class TraversalTest(fixtures.TestBase, AssertsExecutionResults):
     """test ClauseVisitor's traversal, particularly its 
@@ -1074,6 +1074,18 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL):
         assert s2._execution_options == dict(foo='bar', bar='baz')
         assert s3._execution_options == dict(foo='not bar')
 
+    def test_invalid_options(self):
+        assert_raises(
+            exc.ArgumentError,
+            select().execution_options, compiled_cache={}
+        )
+
+        assert_raises(
+            exc.ArgumentError,
+            select().execution_options, 
+                isolation_level='READ_COMMITTED'
+        )
+
     # this feature not available yet
     def _NOTYET_test_execution_options_in_kwargs(self):
         s = select(execution_options=dict(foo='bar'))