]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Connection has execution_options(), generative method
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 24 Jan 2010 22:50:58 +0000 (22:50 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 24 Jan 2010 22:50:58 +0000 (22:50 +0000)
which accepts keywords that affect how the statement
is executed w.r.t. the DBAPI.   Currently supports
"stream_results", causes psycopg2 to use a server
side cursor for that statement.   Can also be set
upon select() and text() constructs directly as well
as ORM Query().

CHANGES
lib/sqlalchemy/dialects/mysql/oursql.py
lib/sqlalchemy/engine/base.py
lib/sqlalchemy/engine/default.py
lib/sqlalchemy/sql/expression.py
lib/sqlalchemy/util.py
test/dialect/test_postgresql.py

diff --git a/CHANGES b/CHANGES
index 021cd4a236bb830d7a8cd9f01950d445b1fba523..a0b25bce092282f89b68a293f82ab47cbcc3d1fe 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -309,9 +309,8 @@ CHANGES
        as appropriate for more complex situations.
        [ticket:1628]
 
-    - Added "execution_options()" to Selects, which set statement
-      specific options. These enable e.g. dialect specific options
-      such as whether to enable using server side cursors, etc.
+    - Added "execution_options()" to select()/text(), which set the
+      default options for the Connection.  See the note in "engines".
        
     - Deprecated or removed:
         * "scalar" flag on select() is removed, use 
@@ -328,6 +327,14 @@ CHANGES
     create_engine(... isolation_level="..."); available on
     postgresql and sqlite. [ticket:443]
   
+  - Connection has execution_options(), generative method
+    which accepts keywords that affect how the statement 
+    is executed w.r.t. the DBAPI.   Currently supports 
+    "stream_results", causes psycopg2 to use a server
+    side cursor for that statement.   Can also be set
+    upon select() and text() constructs directly as well
+    as ORM Query().
+    
   - fixed the import for entrypoint-driven dialects to 
     not rely upon silly tb_info trick to determine import
     error status.  [ticket:1630]
index 70bec53a3c08481c563135a5e407185fe76e2137..dc1310db7e61caaa03c1232ad2dbfad31696ef2a 100644 (file)
@@ -54,7 +54,7 @@ class MySQL_oursqlExecutionContext(MySQLExecutionContext):
     
     @property
     def plain_query(self):
-        return self._connection.options.get('plain_query', False)
+        return self.execution_options.get('_oursql_plain_query', False)
 
 
 class MySQL_oursql(MySQLDialect):
@@ -90,7 +90,7 @@ class MySQL_oursql(MySQLDialect):
         connection.cursor().execute('BEGIN', plain_query=True)
 
     def _xa_query(self, connection, query, xid):
-        connection._with_options(plain_query=True).execute(query % connection.connection._escape_string(xid))
+        connection.execution_options(_oursql_plain_query=True).execute(query % connection.connection._escape_string(xid))
 
     # Because mysql is bad, these methods have to be reimplemented to use _PlainQuery. Basically, some queries
     # refuse to return any data if they're run through the parameterized query API, or refuse to be parameterized
@@ -115,12 +115,12 @@ class MySQL_oursql(MySQLDialect):
         self._xa_query(connection, 'XA COMMIT "%s"', xid)
 
     def has_table(self, connection, table_name, schema=None):
-        return MySQLDialect.has_table(self, connection._with_options(plain_query=True), table_name, schema)
+        return MySQLDialect.has_table(self, connection.execution_options(_oursql_plain_query=True), table_name, schema)
 
     def _show_create_table(self, connection, table, charset=None,
                            full_name=None):
         return MySQLDialect._show_create_table(self, 
-            connection.contextual_connect(close_with_result=True)._with_options(plain_query=True), 
+            connection.contextual_connect(close_with_result=True).execution_options(_oursql_plain_query=True), 
             table, charset, full_name)
 
     def is_disconnect(self, e):
index e74e00d84c0ad1e4c8e057f5ea355b80e0abd9bb..3d192a9be6fba3bd5b9c2aad9b0465bf8f5c6dc3 100644 (file)
@@ -719,10 +719,10 @@ class Connection(Connectable):
     .. index::
       single: thread safety; Connection
     """
-    options = {}
+    _execution_options = util.frozendict()
     
     def __init__(self, engine, connection=None, close_with_result=False,
-                 _branch=False, _options=None):
+                 _branch=False, _execution_options=None):
         """Construct a new Connection.
 
         Connection objects are typically constructed by an
@@ -736,8 +736,8 @@ class Connection(Connectable):
         self.__savepoint_seq = 0
         self.__branch = _branch
         self.__invalid = False
-        if _options:
-            self.options = _options
+        if _execution_options:
+            self._execution_options = self._execution_options.union(_execution_options)
 
     def _branch(self):
         """Return a new Connection which references this Connection's
@@ -750,7 +750,7 @@ class Connection(Connectable):
 
         return self.engine.Connection(self.engine, self.__connection, _branch=True)
     
-    def _with_options(self, **opt):
+    def execution_options(self, **opt):
         """Add keyword options to a Connection generatively.
         
         Experimental.  May change the name/signature at 
@@ -763,7 +763,7 @@ class Connection(Connectable):
         """
         return self.engine.Connection(
                     self.engine, self.__connection,
-                     _branch=self.__branch, _options=opt)
+                     _branch=self.__branch, _execution_options=opt)
     
     @property
     def dialect(self):
index bb3688597aea1a70e6e085b060d507fc7cb75431..6db65583219a20459be9f4e6ffd471f8d630000c 100644 (file)
@@ -224,13 +224,13 @@ class DefaultDialect(base.Dialect):
 
 
 class DefaultExecutionContext(base.ExecutionContext):
-    execution_options = util.frozendict()
     
     def __init__(self, dialect, connection, compiled_sql=None, compiled_ddl=None, statement=None, parameters=None):
         self.dialect = dialect
         self._connection = self.root_connection = connection
         self.engine = connection.engine
-
+        self.execution_options = connection._execution_options
+        
         if compiled_ddl is not None:
             self.compiled = compiled = compiled_ddl
             if not dialect.supports_unicode_statements:
@@ -268,7 +268,8 @@ class DefaultExecutionContext(base.ExecutionContext):
             self.isinsert = compiled.isinsert
             self.isupdate = compiled.isupdate
             self.isdelete = compiled.isdelete
-            self.execution_options = compiled.statement._execution_options
+            self.execution_options =\
+                    compiled.statement._execution_options.union(self.execution_options)
 
             if not parameters:
                 self.compiled_parameters = [compiled.construct_params()]
index f2ad4351a2a0e2b02ef0296cd9018086db052dd3..eb64fd571b4077627cf74f98fb1a741c1e06d0d6 100644 (file)
@@ -2194,16 +2194,13 @@ class _Executable(object):
     _execution_options = util.frozendict()
 
     @_generative
-    def execution_options(self, **kwargs):
+    def execution_options(self, **kw):
         """ Set non-SQL options for the statement, such as dialect-specific options.
 
         The options available are covered in the respective dialect's section.
 
         """
-        _execution_options = self._execution_options.copy()
-        for key, value in kwargs.items():
-            _execution_options[key] = value
-        self._execution_options = _execution_options
+        self._execution_options = self._execution_options.union(kw)
 
     
 class _TextClause(_Executable, ClauseElement):
@@ -2252,6 +2249,11 @@ class _TextClause(_Executable, ClauseElement):
         else:
             return None
 
+    def _generate(self):
+        s = self.__class__.__new__(self.__class__)
+        s.__dict__ = self.__dict__.copy()
+        return s
+
     def _copy_internals(self, clone=_clone):
         self.bindparams = dict((b.key, clone(b))
                                for b in self.bindparams.values())
index cfa891554f1cd85912f6568a2007a4b996405d68..c2ef8142502cf7124fdce4d8d22ad040870e36d1 100644 (file)
@@ -154,6 +154,14 @@ class frozendict(dict):
     def __init__(self, *args):
         pass
 
+    def union(self, d):
+        if not self:
+            return frozendict(d)
+        else:
+            d2 = self.copy()
+            d2.update(d)
+            return frozendict(d2)
+            
     def __repr__(self):
         return "frozendict(%s)" % dict.__repr__(self)
 
index c0a93afcfd17a6fde1ad1602a560a2a8ca2ee87a..952f633ae3340e1220f414b73e6e0a94ef8a491a 100644 (file)
@@ -1461,6 +1461,14 @@ class ServerSideCursorsTest(TestBase, AssertsExecutionResults):
         # ... but enabled for this one.
         assert result.cursor.name
 
+        # and this one
+        result = engine.connect().execution_options(stream_results=True).execute("select 1")
+        assert result.cursor.name
+        
+        # not this one
+        result = engine.connect().execution_options(stream_results=False).execute(s)
+        assert not result.cursor.name
+        
     def test_ss_explicitly_disabled(self):
         s = select([1]).execution_options(stream_results=False)
         result = ss_engine.execute(s)
@@ -1517,7 +1525,7 @@ class ServerSideCursorsTest(TestBase, AssertsExecutionResults):
         s = text('select 42')
         result = engine.execute(s)
         assert not result.cursor.name
-        s = text('select 42', execution_options=dict(stream_results=True))
+        s = text('select 42').execution_options(stream_results=True)
         result = engine.execute(s)
         assert result.cursor.name