]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- the "autocommit" flag on select() and text() as well
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 28 Jan 2010 23:49:22 +0000 (23:49 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 28 Jan 2010 23:49:22 +0000 (23:49 +0000)
      as select().autocommit() are deprecated - now call
      .execution_options(autocommit=True) on either of those
      constructs, also available directly on Connection and orm.Query.

CHANGES
lib/sqlalchemy/engine/default.py
lib/sqlalchemy/schema.py
lib/sqlalchemy/sql/expression.py
test/engine/test_transaction.py

diff --git a/CHANGES b/CHANGES
index f8ebfbb41b730785a94c7984840cad4e514a8546..aef444c38a23a2f299f69a7201784a5eaafe4f65 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -240,6 +240,12 @@ CHANGES
         Use the 'bind' keyword argument.
         
 - sql
+  
+    - the "autocommit" flag on select() and text() as well
+      as select().autocommit() are deprecated - now call
+      .execution_options(autocommit=True) on either of those
+      constructs, also available directly on Connection and orm.Query.
+
     - the autoincrement flag on column now indicates the column
       which should be linked to cursor.lastrowid, if that method
       is used.  See the API docs for details.
@@ -349,14 +355,16 @@ CHANGES
   - transaction isolation level may be specified with
     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().
+    side cursor for that statement, as well as 
+    "autocommit", which is the new location for the "autocommit"
+    option from select() and text().   select() and
+    text() also have .execution_options() as well as 
+    ORM Query().
     
   - fixed the import for entrypoint-driven dialects to 
     not rely upon silly tb_info trick to determine import
index e168a54655bf0f1cf0f247e276bf304419341264..e2a03d22769461627715b900494ca8741260ca7b 100644 (file)
@@ -229,7 +229,6 @@ class DefaultExecutionContext(base.ExecutionContext):
         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
@@ -268,9 +267,6 @@ class DefaultExecutionContext(base.ExecutionContext):
             self.isinsert = compiled.isinsert
             self.isupdate = compiled.isupdate
             self.isdelete = compiled.isdelete
-            if compiled.statement._execution_options:
-                self.execution_options =\
-                        compiled.statement._execution_options.union(self.execution_options)
 
             if not parameters:
                 self.compiled_parameters = [compiled.construct_params()]
@@ -301,18 +297,24 @@ class DefaultExecutionContext(base.ExecutionContext):
             self.cursor = self.create_cursor()
 
     @util.memoized_property
-    def should_autocommit(self):
+    def execution_options(self):
         
         if self.compiled:
-            autocommit = self.compiled.statement._autocommit
-            if autocommit is expression.PARSE_AUTOCOMMIT:
+            return self.compiled.statement._execution_options.union(
+                            self._connection._execution_options)
+        else:
+            return self._connection._execution_options
+            
+    @util.memoized_property
+    def should_autocommit(self):
+        autocommit = self.execution_options.get('autocommit', expression.PARSE_AUTOCOMMIT)
+        if autocommit is expression.PARSE_AUTOCOMMIT:
+            if self.statement:
                 return self.should_autocommit_text(self.statement)
             else:
-                return autocommit
-        elif self.statement:
-            return self.should_autocommit_text(self.statement)
+                return False
         else:
-            return False
+            return autocommit
             
     @util.memoized_property
     def _is_explicit_returning(self):
index f2737ecde56368eceecdb4d1677f74c57ce2f1b7..cd9bd48921a80ce3be98d177a548610519bfbec8 100644 (file)
@@ -2035,7 +2035,8 @@ class SchemaVisitor(visitors.ClauseVisitor):
 class DDLElement(expression._Executable, expression.ClauseElement):
     """Base class for DDL expression constructs."""
     
-    _autocommit = True
+    _execution_options = expression._Executable.\
+                            _execution_options.union({'autocommit':True})
 
     target = None
     on = None
index a4504e83303f2ec09c9031de8304457558e28a81..5edb6e47fedfeef7844b79f3fd8a9913962d92be 100644 (file)
@@ -164,9 +164,8 @@ def select(columns=None, whereclause=None, from_obj=[], **kwargs):
       Additional parameters include:
 
       autocommit
-        indicates this SELECT statement modifies the database, and
-        should be subject to autocommit behavior if no transaction
-        has been started.
+        Deprecated.  Use .execution_options(autocommit=<True|False>)
+        to set the autocommit option.
 
       prefixes
         a list of strings or :class:`ClauseElement` objects to include
@@ -805,9 +804,8 @@ def text(text, bind=None, *args, **kwargs):
       an optional connection or engine to be used for this text query.
 
     autocommit=True
-      indicates this SELECT statement modifies the database, and
-      should be subject to autocommit behavior if no transaction
-      has been started.
+      Deprecated.  Use .execution_options(autocommit=<True|False>)
+      to set the autocommit option.
 
     bindparams
       a list of :func:`bindparam()` instances which can be used to define
@@ -2215,9 +2213,26 @@ class _Executable(object):
 
     @_generative
     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.
+        """ Set non-SQL options for the statement which take effect during execution.
+        
+        Current options include:
+        
+        * 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 invokving a SELECT or other specific SQL construct
+          where COMMIT is desired (typically when calling stored procedures
+          and such).
+          
+        * 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.
 
         """
         self._execution_options = self._execution_options.union(kw)
@@ -2233,7 +2248,8 @@ class _TextClause(_Executable, ClauseElement):
     __visit_name__ = 'textclause'
 
     _bind_params_regex = re.compile(r'(?<![:\w\x5c]):(\w+)(?!:)', re.UNICODE)
-
+    _execution_options = _Executable._execution_options.union({'autocommit':PARSE_AUTOCOMMIT})
+    
     @property
     def _select_iterable(self):
         return (self,)
@@ -2242,11 +2258,16 @@ class _TextClause(_Executable, ClauseElement):
 
     def __init__(self, text = "", bind=None, 
                     bindparams=None, typemap=None, 
-                    autocommit=PARSE_AUTOCOMMIT):
+                    autocommit=None):
         self._bind = bind
         self.bindparams = {}
         self.typemap = typemap
-        self._autocommit = autocommit
+
+        if autocommit is not None:
+            util.warn_deprecated("autocommit on text() is deprecated.  "
+                                        "Use .execution_options(autocommit=True)")
+            self._execution_options = self._execution_options.union({'autocommit':autocommit})
+        
         if typemap is not None:
             for key in typemap.keys():
                 typemap[key] = sqltypes.to_instance(typemap[key])
@@ -2856,7 +2877,6 @@ class Alias(FromClause):
         self.original = baseselectable
         self.supports_execution = baseselectable.supports_execution
         if self.supports_execution:
-            self._autocommit = baseselectable._autocommit
             self._execution_options = baseselectable._execution_options
         self.element = selectable
         if alias is None:
@@ -3229,10 +3249,13 @@ class _SelectBaseMixin(_Executable):
             order_by=None,
             group_by=None,
             bind=None,
-            autocommit=False):
+            autocommit=None):
         self.use_labels = use_labels
         self.for_update = for_update
-        self._autocommit = autocommit
+        if autocommit is not None:
+            util.warn_deprecated("autocommit on select() is deprecated.  "
+                                        "Use .execution_options(autocommit=True)")
+            self._execution_options = self._execution_options.union({'autocommit':autocommit})
         self._limit = limit
         self._offset = offset
         self._bind = bind
@@ -3276,10 +3299,12 @@ class _SelectBaseMixin(_Executable):
         return self.as_scalar().label(name)
 
     @_generative
+    @util.deprecated(message="autocommit() is deprecated. "
+                        "Use .execution_options(autocommit=True)")
     def autocommit(self):
         """return a new selectable with the 'autocommit' flag set to True."""
-
-        self._autocommit = True
+        
+        self._execution_options = self._execution_options.union({'autocommit':True})
 
     def _generate(self):
         s = self.__class__.__new__(self.__class__)
@@ -3864,7 +3889,7 @@ class _UpdateBase(_Executable, ClauseElement):
 
     __visit_name__ = 'update_base'
 
-    _autocommit = True
+    _execution_options = _Executable._execution_options.union({'autocommit':True})
     
     def _generate(self):
         s = self.__class__.__new__(self.__class__)
@@ -4118,7 +4143,7 @@ class Delete(_UpdateBase):
 
 class _IdentifiedClause(_Executable, ClauseElement):
     __visit_name__ = 'identified'
-    _autocommit = False
+    _execution_options = _Executable._execution_options.union({'autocommit':False})
     quote = None
 
     def __init__(self, ident):
index a58d65e68adb7c7c47ec796cfaf9824e77a4b0a0..fa2cc3e59cdf9c9c175ed21adcdf38acfc359364 100644 (file)
@@ -369,7 +369,6 @@ class AutoRollbackTest(TestBase):
         users.drop(conn2)
         conn2.close()
 
-foo = None
 class ExplicitAutoCommitTest(TestBase):
     """test the 'autocommit' flag on select() and text() objects.
 
@@ -382,9 +381,13 @@ class ExplicitAutoCommitTest(TestBase):
     def setup_class(cls):
         global metadata, foo
         metadata = MetaData(testing.db)
-        foo = Table('foo', metadata, Column('id', Integer, primary_key=True), Column('data', String(100)))
+        foo = Table('foo', metadata, 
+                        Column('id', Integer, primary_key=True), 
+                        Column('data', String(100))
+                    )
         metadata.create_all()
-        testing.db.execute("create function insert_foo(varchar) returns integer as 'insert into foo(data) values ($1);select 1;' language sql")
+        testing.db.execute("create function insert_foo(varchar) returns integer "
+                            "as 'insert into foo(data) values ($1);select 1;' language sql")
 
     def teardown(self):
         foo.delete().execute().close()
@@ -417,6 +420,67 @@ class ExplicitAutoCommitTest(TestBase):
         conn1 = testing.db.connect()
         conn2 = testing.db.connect()
 
+        conn1.execute(select([func.insert_foo('data1')]).execution_options(autocommit=True))
+        assert conn2.execute(select([foo.c.data])).fetchall() == [('data1',)]
+
+        conn1.close()
+        conn2.close()
+
+    def test_explicit_connection(self):
+        conn1 = testing.db.connect()
+        conn2 = testing.db.connect()
+
+        conn1.execution_options(autocommit=True).\
+                        execute(select([func.insert_foo('data1')]))
+        eq_(
+            conn2.execute(select([foo.c.data])).fetchall(),
+            [('data1',), ]
+        )
+
+        # connection supercedes statement
+        conn1.execution_options(autocommit=False).\
+                        execute(
+                            select([func.insert_foo('data2')]).
+                                execution_options(autocommit=True)
+                        )
+        eq_(
+            conn2.execute(select([foo.c.data])).fetchall(),
+            [('data1',), ]
+        )
+        
+        # ditto
+        conn1.execution_options(autocommit=True).\
+                        execute(
+                            select([func.insert_foo('data3')]).
+                                execution_options(autocommit=False)
+                        )
+        eq_(
+            conn2.execute(select([foo.c.data])).fetchall(),
+            [('data1',), ('data2', ), ('data3',)] 
+        )
+
+        conn1.close()
+        conn2.close()
+
+    def test_explicit_text(self):
+        conn1 = testing.db.connect()
+        conn2 = testing.db.connect()
+
+        conn1.execute(
+                    text("select insert_foo('moredata')").
+                    execution_options(autocommit=True)
+                )
+        assert conn2.execute(select([foo.c.data])).fetchall() == [('moredata',)]
+
+        conn1.close()
+        conn2.close()
+
+    @testing.uses_deprecated(r'autocommit on select\(\) is deprecated',
+                            r'autocommit\(\) is deprecated')
+    def test_explicit_compiled_deprecated(self):
+        conn1 = testing.db.connect()
+        conn2 = testing.db.connect()
+
         conn1.execute(select([func.insert_foo('data1')], autocommit=True))
         assert conn2.execute(select([foo.c.data])).fetchall() == [('data1',)]
 
@@ -426,7 +490,8 @@ class ExplicitAutoCommitTest(TestBase):
         conn1.close()
         conn2.close()
 
-    def test_explicit_text(self):
+    @testing.uses_deprecated(r'autocommit on text\(\) is deprecated')
+    def test_explicit_text_deprecated(self):
         conn1 = testing.db.connect()
         conn2 = testing.db.connect()